Первый публичный релизенг. См. прикрепление. К сожалению, только Windows/x86 пока что. Интересно знать, запустится ли на других машинах вообще (не проверял). (И что насчёт WOW64?)
Фичи:
* статически типизированный
* объектно-ориентированный во все поля (по семантике близок к C#)
* garbage-collected
* рефлективный
* JIT-компилируемый
* легко встраиваемый, легковесный (виртуальная машина - 472 КБ), расширяемый
* большой фокус на замыканиях
Основное применение - небольшие такие обёртки вокруг нативного кода и просто для l33t-хэкинга.
Особенности
- Синтаксис простой: (объект метод аргумент1 аргумент2 аргумент3). Это позволяет унифицировать методы с арифметическими операциями: выражение "(1 + 2)" есть вызов метода "+" у объекта "1". Нет operator precedence, так как нет понятия "оператор", всё группируется скобочками, как в Lisp'е (за исключением того, что top-level выражения скобочки не требует)
- Отсутствует специальный синтаксис для if..then или while. Вместо этого, как в Smalltalk, используется вызов методов + замыкания. Например:
Цитировать(0 to n) loop ^(i: int) { };
(0 to n) есть вызов метода "to" у объекта 0, что создаёт объект класса Range. Объект класса Range имеет метод "loop", который принимает замыкание и проходится по каждому элементу Range, применяя замыкание к каждому элементу. Это всё inlinable, правда в текущем релизе инлайнится только bool::then
- Типы по значению (valuetypes) - обитают в стеке, иммутабельны. Прекрасны для Vector/Matrix в игровых движках, например. Выше указанный Range является valuetype. Типы по значению являются самыми обычными типами - они, например, могут реализовывать интерфейсы.
- Неявные интерфейсы (как оказалось, в Go придумали то же самое). Если класс располагает набором методов, указанным в интерфейсе, то такой класс автоматически реализует интерфейс. Это можно назвать static duck typing.
- Отсутствует унифицированная иерархия классов как в C#/Java. Это нам не нужно, так как абсолютно все типы автоматически приводятся к пустому неявному интерфейсу any.
- Отсутствует различие между user-классами и primitive-классами. Целые числа, например, есть такие же объекты с такими же правилами, как и у остальных объектов - при этом overhead нулевой.
- Именованные конструкторы (при вызове выглядят, как factory methods.)
- Прекрасен в качестве обёртки нативного кода: существует 4 способа обратиться к нативному (С) коду:
а) icalls, на уровне C API
б) ecalls, то есть external calls, позволяют вызывать методы в нативных DLL прямо из-под ГриньСкрипта без glue-кода
в) inline C
г) inline ASM внутри inline C
- Failables - алгебраический тип данных, который содержит или значение, или ошибку. Наш ответ "исключениям". Например, тип int? может содержать как объект ошибки в случае неудачи, так и целочисленное значение в случае удачи.
- Домены - виртуальная машина разделена на т.н. домены, что есть изолированные экземпляры VM в собственных потоках. Домены полностью независимы друг от друга и реализуют идею многопоточности, а также sandboxing. Домены могут многопоточно общаться друг с другом с помощью экспортирования объектов в другие домены как "чужих". Методы "чужих" объектов реализуются как Remote Procedure Call, как синхронные, так и асинхронные (в данном релизе асинхронные вызовы так и не реализованы).
- Рефлексия, атрибуты и т. д. (в данной версии классы рефлексии не до конца реализованы)
- Портабельность VM в смысле Ctrl+C/Ctrl+V без нужды что-либо устанавливать.
- Много семантических деталей есть по мелочи, типа поля могут быть только приватными. Или возможность в compile-time добавить новые методы примитивным типам типа int, bool и т.д. с нулём оверхеда.
В данной версии есть известные проблемы/баги:
а) Запуск скрипта увеличивается прямо пропорционально размеру скрипта, так как всё предкомпилируется сразу (за исключением managed-to-native callback transition thunks). В данный момент я это обхожу с помощью SplashScreen's (не включено в данный релиз)
б) Если valuetypes рекурсивно ссылаются друг на друга, может произойти segfault (руки не дошли).
в) Сборщик мусора примитивный, но пока мне хватает.
г) Стандартная библиотека недоделанная и куцая (руки не дошли).
В релизе есть следующие файлы
ussr.exe - запускатель скриптов в консоли
ussr-win.exe - запускатель скриптов без консоли
ussrvm10.dll - сама виртуальная машина, независима от ussr.exe
here.bat - для удобного открытия консоли в текущей папке
run-helloworld.bat - сюда можно тыкнуть в первый раз для проверки работы, должен появиться MessageBox. Здесь же можно посмотреть, как вызывать скрипты (ussr /source:path)
папка
base - базовые классы
папка
examples - примеры на синтаксис и семантику
папка
misc/sovietscript.h - заголовочные файлы для общения с VM напрямую (недотестено)
папка
misc/sovietscript_notepad++_theme.xml - тема для подсветки синтаксиса Notepad++
Как-то неинтересно. Вы бы лучше написали бы понятный язык программирования для страдающих математическим кретинизмом. Было бы куда полезнее. :yes:
Цитата: Wolliger Mensch от января 17, 2015, 17:30
Как-то неинтересно
Тут не в интересности дело, а в решении конкретных задач: скриптование С-кода без тонн костылей, как это любят другие языки программирования.
Цитата: Wolliger Mensch от января 17, 2015, 17:30
для страдающих математическим кретинизмом
Это вы о себе? (http://lingvowiki.info/wiki/images/5/5a/Sm_eek.svg)
Цитата: Алексей Гринь от января 17, 2015, 17:21
ussr
Как расшифровывается?
Цитата: Тайльнемер от января 17, 2015, 18:54
Как расшифровывается?
The Union of Soviet Socialist Republics
Цитата: Тайльнемер от января 17, 2015, 18:54
Это вы о себе? (http://lingvowiki.info/wiki/images/5/5a/Sm_eek.svg)
Ну как сказать. Механически запоминая, я осиливал даже интегралы в своё время и какие-то неестественные для себя геометрические загогулины. И программировал для своих нужд, также опираясь на механическое запоминание и широкую аналогию в действиях. Но понятно было, что далеко так не уедешь. Всегда мечтал о
понятном языке программирования. С большим чаянием читал про якобы простые и понятные широким массам языки программирования. Всё оказалось блефом: возможно, это языки действительно простые — для людей с математическим складом ума. Но за их пределы простота, по-моему, никогда не выходила. :'(
Цитата: Алексей Гринь от января 17, 2015, 18:11
Тут не в интересности дело...
Как это? Нет в жизни человека вещей, отношение к которым не зависело бы от интересности.
Цитата: Wolliger Mensch от января 17, 2015, 20:59
Как это? Нет в жизни человека вещей, отношение к которым не зависело бы от интересности.
Есть ещё просто желание сделать чистую легковесную систему под себя, без миллиарда костылей и непонятных зависимостей и registry и whatnot.
Хочется жить в чистоте, а не в грязи.
Цитата: Wolliger Mensch от января 17, 2015, 20:58
Всегда мечтал о понятном языке программирования. С большим чаянием читал про якобы простые и понятные широким массам языки программирования. Всё оказалось блефом: возможно, это языки действительно простые — для людей с математическим складом ума. Но за их пределы простота, по-моему, никогда не выходила. :'(
Э, большое количество кода мало имеет общего с математикой. Это просто объекты и команды к ним на вполне понятном английском языке. Можно спокойно программировать сложные приложения, почти ничего не шаря в математике. Просто используя готовые библиотеки. То есть многие приложения это просто набор готовых блоков, которых ты соединяешь собственным кодом-«клеем».
Ну вот что непонятного в:
окно = создатьОкно();
окно.показать();
?
Другое дело когда пишешь middleware, типа моего вот ГриньСкрипта, или мой UI-фреймворк с 3d-ускорением, тут нужно знать множество тонкостей. Это я один такой дурак; другие же люди не парятся и используют готовые middleware (блоки кода).
Цитата: Алексей Гринь от января 17, 2015, 21:16
Ну вот что непонятного в:
окно = создатьОкно();
окно.показать();
Зачем там равно? Что значат скобки? Почему во второй строке точка? (http://www.kolobok.us/smiles/standart/popcorm2.gif)
Цитата: Wolliger Mensch от января 18, 2015, 01:55Зачем там равно?
Чтобы показать что
окно равно результату действия
создатьОкно, которое
Цитата: Wolliger Mensch от января 18, 2015, 01:55значат скобки
Цитата: Wolliger Mensch от января 18, 2015, 01:55Почему во второй строке точка?
Потому, что так разделяются объект
окно и его действие
показать(ся), применение которого (действия)
Цитата: Wolliger Mensch от января 18, 2015, 01:55значат скобки
Цитата: Bhudh от января 18, 2015, 02:49
Чтобы показать что окно равно результату действия создатьОкно, которое
Это не совсем так. Присваивание — это же не равенство в математическом смысле.
Цитата: Wolliger Mensch от января 18, 2015, 01:55
Зачем там равно? Что значат скобки? Почему во второй строке точка?
Равно есть приравнивание.
"окно=создатьОкно()" значит «пусть есть новая переменная (существительное) "окно", и пусть оно будет содержать значение, которое возвратила функция создатьОкно».
Скобки это вызов функции, т.е. глагола. В данном случае нет параметров, поэтому скобки пустые. С помощью скобок мы запускаем глагол, типа повелительного наклонения у глагола.
Точка в "окно.показать();" значит «вызвать глагол под названием "показать", ассоциированный с существительноным "окно"». В объектно-ориентированном программировании глаголы могут быть привязаны к своим словам-хозяинам. То есть, смена контекста. "Показать" может значить одно, если выполняется в контексте "окна", но совсем другое, если выполняется в контексте объекта (существительного) другого типа («показать окно» и «показать фигу» есть разные ситуации, хоть глагол и назван одинаково). То есть, таким образом, многозначность имеем мы.
Цитата: Тайльнемер от января 18, 2015, 05:12Присваивание — это же не равенство в математическом смысле
В момент исполнения — именно равенство. А потом по обстоятельствам.
Цитата: Bhudh от января 18, 2015, 15:46
В момент исполнения — именно равенство. А потом по обстоятельствам.
Равенство чему? Ладно, там, x=5. Но что есть x=foo()?
foo() это не значение, это операция. x не может быть «равна» операции.
Цитата: Алексей Гринь от января 18, 2015, 16:32foo() это не значение, это операция.
Алексей, уж от тебя-то не ожидал! :o
"Операция" (функция) — это
foo. А
foo() — её вызов.
А когда идёт приравнивание переменной
вызову функции, это приравнивание не самой функции, а её
результату.
В момент присваивания значение переменной и значение результата функции равны.
Цитата: Алексей Гринь от января 18, 2015, 11:41
Цитата: Wolliger Mensch от января 18, 2015, 01:55
Зачем там равно? Что значат скобки? Почему во второй строке точка?
Равно есть приравнивание.
"окно=создатьОкно()" значит «пусть есть новая переменная (существительное) "окно", и пусть оно будет содержать значение, которое возвратила функция создатьОкно».
Скобки это вызов функции, т.е. глагола. В данном случае нет параметров, поэтому скобки пустые. С помощью скобок мы запускаем глагол, типа повелительного наклонения у глагола.
Если скобки — это вызов глагола, то при пустых скобках глагола нет. А что такое тогда «создатьОкно»? :what:
Действие само по себе.
Скажем, с реальным окном действие — это, например, выстругивание.
Цитата: Bhudh от января 18, 2015, 17:22
Действие само по себе.
Скажем, с реальным окном действие — это, например, выстругивание.
Непонятно. :donno:
Окно — результат выстругивания. Само выстругивание абстрактно и имеет смысл только при наличии прямого дополнения.
Цитата: Wolliger Mensch от января 18, 2015, 17:17
Если скобки — это вызов глагола, то при пустых скобках глагола нет. А что такое тогда «создатьОкно»? :what:
Вы немного не так поняли.
создатьОкно — это глагол.
Скобки после глагола — это его вызов.
В данном случае глагол непереходный, поэтому скобки пустые, а так, там в скобках могут стоять дополнения через запятую.
Цитата: Тайльнемер от января 18, 2015, 18:27В данном случае глагол непереходный
Переходный, просто уже конкретизированный.
Никто ж не мешает написать
окно = создать("окно");.
Цитата: Bhudh от января 18, 2015, 17:00
Алексей, уж от тебя-то не ожидал! :o
"Операция" (функция) — это foo. А foo() — её вызов.
А когда идёт приравнивание переменной вызову функции, это приравнивание не самой функции, а её результату.
В момент присваивания значение переменной и значение результата функции равны.
Понятие равенства имеет смысл только в математике, где функции это отношения между объектами, которые существуют, как бы сказать, извечно, и отсутствует понятие времени.
Программистское x=foo() не может быть «равенством», так как foo() это последовательная, осуществляемая во времени операция, описывающая не извечное отношение, а императивный алгоритм.
Цитата: Wolliger Mensch от января 18, 2015, 17:17
Если скобки — это вызов глагола, то при пустых скобках глагола нет. А что такое тогда «создатьОкно»? :what:
Ну можно провести аналогию, что вызов функции создатьОкно() есть императив, а функция без скобок — создатьОкно — своего рода инфинитив. Не все языки разрешают такое; те же, что разрешают, рассматривают обращение к функциям без скобок (т.е. к инфинитивам) как самим объектам. То есть происходит субстантивация инфинитивной формы. И тогда можно обращаться с функцией как с объектом (существительным).
Цитата: Алексей Гринь от января 18, 2015, 20:21foo() это последовательная, осуществляемая во времени операция, описывающая не отношение, а императивный алгоритм.
Алгоритм это тащемта мыслимая последовательность событий, записанная в теле функции на ЯПе.
А при вызове функции нет нужды мыслить такими категориями.
Результат вызова
foo(), равный 5, ничем не отличается в каком-либо выражении от значения переменной
foo, равного 5.
Для выражения имеет значение
только возвращаемый результат, который выступает как условное "значение функции". А как именно функция его определяет, это дело десятое. Пусть хоть из константы берёт и возвращает.
Гринь, может, лучше на Хабру со всем этим? :-[
Цитата: Алексей Гринь от января 18, 2015, 20:24То есть происходит субстантивация инфинитивной формы. И тогда можно обращаться с функцией как с объектом (существительным).
А чего сразу существительным-то? У инфинитива тоже могут быть свои свойства: вид, время там, а то и залог...
Это в императиве их чаще всего не бывает.
Цитата: Jumis от января 18, 2015, 20:28может, лучше на Хабру со всем этим? :-[
Там сразу вывесят картинку с 14-ю стандартами.
Цитата: Bhudh от января 18, 2015, 20:27
Результат вызова foo(), равный 5, ничем не отличается в каком-либо выражении от значения переменной foo, равного 5.
В том-то и дело, что нечистые функции могут возвратить то 5, то 3, то ещё чего. Самая переменная x может менять своё значение. Поэтому нельзя говорить, что x=foo() есть равенство, потому что это не так. Это динамические приравнивание, а не статическое отношение равенства.
Цитата: Bhudh от января 18, 2015, 20:30
А чего сразу существительным-то? У инфинитива тоже могут быть свои свойства: вид, время там, а то и залог...
Это в императиве их чаще всего не бывает.
Ну замени инфинитив на герундий. First-class function objects/functors - это по сути программистский аналог герундиев.
Цитата: Jumis от января 18, 2015, 20:28
Гринь, может, лучше на Хабру со всем этим? :-[
Фу.
Цитата: Алексей Гринь от января 18, 2015, 20:34нельзя говорить, что x=foo() есть равенство, потому что это не так. Это динамические приравнивание
Цитата: Bhudh от января 18, 2015, 15:46В момент исполнения
Цитата: Bhudh от января 18, 2015, 21:26
Цитата: Алексей Гринь от января 18, 2015, 20:34нельзя говорить, что x=foo() есть равенство, потому что это не так. Это динамические приравнивание
Цитата: Bhudh от января 18, 2015, 15:46В момент исполнения
Равенство - метод (если говорить в парадигме Гринь-скрипта), возвращающий булево значение. Присвоение возвращает собственно присвоенное значение (ну, или ошибку). Разница как между словосочетанием и предикативным сочетанием.
Цитата: Алексей Гринь от января 18, 2015, 20:24
можно провести аналогию, что вызов функции создатьОкно() есть императив, а функция без скобок — создатьОкно — своего рода инфинитив
А можете, в рамках Вашей аналогии, пояснить лямбда-функцию?
А чем λ-действие так сильно отличается от обычного действия, кроме того, что придумывается на ходу?
Обновил версию в прикреплении.
- Пофиксил баг, заключающийся в том, что запрещённый implicit unboxing пытался скомпилироваться в некорректный код, вместо того, чтобы прекращать компиляцию.
- Не помню, что было в первоначальном релизенге, но только сейчас я обнаружил, что в closure-to-callback transition thunks используемые регистры не сохранялись корректно. Каким чудом это работало, не знаю (тестирую на сравнительно большом проекте, и там ничего не вылетало, чудеса). Также уменьшил размер thunk'ов с ~50 байт до 29.
- Оптимизировал немного внутренние структуры VM, так что использует памяти на 15-20% меньше, однако это поделие всё равно жрёт слишком много под себя. Есть экстремальный скрипт размером в 6.8 мегабайт, так вот это поделие всё равно жрёт в районе 450 МБ чисто под свои внутренние структуры (раньше жрало и вовсе 550 МБ до оптимизаций). Всё дело в том, что все токены и все expressions сохраняются в памяти и никуда не удаляются (пока не удалится домен) и существуют в не очень оптимизированном формате. Надо тут как-то решать (но надо ли? нужны ли нам скрипты размеров 7 МБ?)
- Добавил новый немного шизоидный пример, в котором числа Фибоначчи возвращаются и результаты кэшируются путём создания замыкания, которое возвращает замыкание. Чисто тест на корректность.
- Устранил утечки памяти.
- Нативные библиотеки теперь кэшированно загружаются и отгружаются корректно при удалении доменов. Раньше это всё аккумулировалось.
- Простые геттер-методы, оборачивающие доступ к переменным, теперь генерируется напрямую ручным JIT'ом в обход C-компилятора в RAM, чтобы снять давление на него (и это только начало). Чтобы сильно не заморачиваться, не все геттеры компилируются так, часть всё равно реализуется через C-компилятор.
- Добавил новый оператор sizeof.
Обновил версию в прикреплении.
1) Ускорил время загрузки в 1.5 раза (за счёт переноса ответственности части кода с C compiler на ThunkManager). Собственно компиляция (не считая парсинга и верификации) того 7-мегабайтного примера теперь занимает меньше 5 секунд (аналог в С занимает 35 мегабайт). В будущем будем оптимизировать парсинг (прекэширование структур?)
2) Рудиментарная поддержка отладки прямо изнутри языка: если включён softdebug:true, то специальный оператор break остановит текущий домен и перечислит все локальные переменные в текущем контексте, а также их значения, и далее можно вводить команды (из-под консоли): пока что можно только перечислять свойства локальных переменных и смотреть их значение; в будущем расширю список. Среди ограничений пока что: 1) захваченные переменные не включается в список 2) не выводятся значения valuetypes 3) массивы распознаются при выводе значений, но не Map'ы (в след. версии)
Если softdebug:true не включён, то все операторы break игнорируются (аналогично assert в C)
3) Исправил разные баги (например, имена аргументов могли конфликтовать с inline C)
Цитата: Rusiok от января 18, 2015, 22:53
А можете, в рамках Вашей аналогии, пояснить лямбда-функцию?
Не совсем в рамках этой аналогии, но вот что-то вроде (http://lingvoforum.net/index.php/topic,79030.html)...