Угу. Меня бы больше приколол скриптовый язык со строгой типизацией и JIT-компиляцией перед началом выполнения. И синтаксис чтоб даже не сишный, а… нет, паскаль для скриптоты слишком многословен… что-то типа Rust, Go.
Только хуже, так как в перле разные операторы для сторокового и численного сравнения, что позволяет избежать огромного количества граблей из-за неявного приведения типов. А еще есть объявление my, позволяющее явно увидеть область действия локальной переменной (sh тоже так умеет, ключевое слово local), а так же иметь полноценные лямбда-выражения (в sh вряд ли получится)
Меня бы больше приколол скриптовый язык со строгой типизацией
Строгой или статической? Язык со статической типизацией всегда будет многословнее, чем язык с динамической типизацией. JIT-компиляция нужна только там, где код выполянется более одного раза, в случае скриптоты это далеко не всегда так, а в случае шелл-скриптов вообще основную работу делают вызываемые программы, а произвордительность самого скрипта ни на что не влияет
что-то типа Rust, Go
Есть (странные) люди, которые go как скриптовый язык используют
Взаимоисключающие параграфы.
Компиляция перед началом выполнения - это AOT.
А JIT это непосредственно во время и по ходу выполнения. JIT компилирует код кусками, не компилирует то, что не используется или используется редко, трассирует часто используемое и перекомпилирует на основе статистики.
Бывает связка AOT + JIT. Но так или иначе стадия компиляции перед началом выполнения - это AOT-компиляция.
P.S. Например, всем известно, что v8 имеет на борту мощный JIT компилятор и использует его во всю. Но мало кто знает, что v8 поддерживает и AOT компиляцию. Именно так устроено кэширование скриптов, v8 кэширует уже скомпилированный инвариант кода, который потом в рантайме может дополнительно оптимизировать JIT’ом. Поэтому на самом деле хромообразные браузеры не переразбирают и неперекомпилируют js при каждом открытии сайтов.
Ровно по такому же принципу очень долго время хранилась и загружалась стандартная библиотека JS, потому как написана она тоже на js (она компилировалась при первом запуске и позднее использовалась ее aot-версия. Сейчас в v8 большая часть стандартной либы, в том числе инициализация хост-объектов (объектов js) вынесено в плюсовый код). Ровно таким же образом хранится стандартная библиотека node.js.
И использовать AOT компилцию в v8 можно самому если нужные API прокинуты в рантайм - в v8 это Snapshot API, и он доступен напрямую из пользовательского кода, например в Node.JS.
При этом можно в любой момент работы скрипта выгрузить snapshot, а потом при следующем старте загрузится в него. При этом будет сохранен весь контекст окружения, который был на момент выгрузки (разумеется со своими ограничениями на разного рода внешние сущности вроде дескрипторов файлов, подключений, шаренной памяти и прочего, что рантаймозависимо - их восстановление должно предусматриваться разработчиком, а снапшоты сохраняют состояния нативных js-объектов, функций, областей видимости и прочего. ну и сам машинный и промежуточный байт-код уже разобранных функций. разумеется снапшоты платформозависимы)
Знал бы зачем read-eval-print loop нужно - может быть и расстроился бы.
Небольшое неудобство только в том, что насмерть захардкожено AFF_TYPE_C. Надо или *.c, или *.i, или без fileextension. Как запустить *.sh (например) шебангом без редактирования исходников tcc я так и не понял пока.
Если есть системный вызов и tcc может дёрнуть соответствующую функцию из стандартной библиотеки, то именно это он и сделает. Самый простой пример на основе кода чуть выше (скрипт находится в файле test):
#!/usr/bin/tcc -run
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <memory.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int main(int argc, char **argv) {
char hostname[1024];
struct addrinfo hints, *info, *p;
int gai_result;
printf("Hello world 1!\n");
printf("Arg: %s\n", argc>1?argv[1]:"-=-");
printf("Calculating sample: %f\n", sqrt(4)); /* This */
/* This shouldn't work without of https at localhost, Toxo2. */
hostname[1023] = '\0';
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
if((gai_result = getaddrinfo(hostname, "https", &hints, &info))!= 0) {
fprintf(stderr, "Result: %s\n", gai_strerror(gai_result));
exit(EXIT_FAILURE);
}
for(p = info; p != NULL; p = p->ai_next) {
fprintf(stderr, "hostname is: %s\n", p->ai_canonname);
}
freeaddrinfo(info);
execve("./test", NULL, NULL); /* And this */
return(0);
}
Для скриптоты на С вполне годно. Если на машине нет сервера с поддержкой https, то работать не будет. На машине с таким сервером всё работает.
Да, сам хотел с места крикнуть tcc, но тут уже опередили, как я погляжу. =)
Наоброт. Указывать шебанг внутри файла со свободным расширением.
Чтобы файлы *.c - это были файлы Си.
А файлы *.c.sh - это были файлы со скриптом на Си. По-моему вполне логичное желание, которое сразу первым в голову приходит. А выясняется что так нельзя.
Ну тебе еще желательно бы сообщить tcc, что ты хочешь от него именно это, а не просто хочешь странного. Так что какой-то ключ специально под это дело я бы таки ввел.
Ваш шебанг не будет работать внутри файла с расширением отличным от ‘c’,‘i’,"
Файл может быть без расширения вовсе, я же там написал прямо – (скрипт находится в файле test). Будет работать. Собственно, проверяется это легко – я не зря там вообще втупую тот же скрипт из самого себя и дёргаю вот так вот нагло – execve("./test", NULL, NULL); /* And this */.
Тут всё связано с общей семантикой решения – считается что код на С является скомпилированным в некий бинарь безо всяких расширений имён файла типа как в DOS/Windows – exe,cmd,bat или ещё что. Просто С-код, который спросто компилирован и исполняется. Не думаю что кто-то, когда создавали bash и другие оболочки, надеялся на то, что кто-то напишет такой интерпретатор С-кода, который в принципе можно будет даже не компилировать. Поэтому не используйте расширения, да и всё. =)
P.S. Теста ради добавил файл t.sh с содержимым:
#!/bin/bash
echo "hi-hi"
И стал вызывать его аналогично – execve("./t.sh", NULL, NULL);. Теперь это скриптовый tcc над нами с Вами ещё и хихикает, гад такой. =)))
Извините, кофе пока варится, у меня утренний тупняк. =)))
Я не обратил внимание на:
А файлы *.c.sh - это были файлы со скриптом на Си.
Можно и так. Пока не готов написать именно как надо в деталях, но общее решение это использовать binfmt_misc. При помощи этого модуля ядра можно как стандартные процессы и скрипты и бинари для отличных от хостовых архитектур гонять как нативные процессы Linux (например, введя команду ./some_arm_app сразу загружать его в QEMU и просматривать по ps -a, там будет не qemu_чёттам, а именно процесс some_arm_app).
Как настраивается такое поведение системы можно прочесть вот тут. Там есть параметр interpreter, там и надо прописать путь /usr/bin/tcc. В общем, примерно понятно как это сделать, делать сам не буду – по мне tcc и сам по себе весьма неплох для моделирования поведения какой-то программы. Понятно что по скорости будем пролетать, но в принципе, прикинуть вариант решения на С вполне годно. Собственно, для чего я его и использую. Quick & dirty solution. =)
Ну право слово – не на питоне же пейсать! Сишнику-то! =)))
В принципе, если просто как интерпретатор С, то можно и tcc -run - использовать и код вбивать прямо в stdin. Если кусок кода короткий и писать без опечаток, то в принципе неплохо. =)
То что он удумал, это «а-та-та» и «ай-яй-яй». В одном флаконе.
То что он задумал реализуется через libtcc, которая есть в наборе. Там и вся нужная генерация С-кода (хоть просто С-кода, хоть с GLSL-шейдерами). И нет нужды в долгом сексе. =)))