Привет! Меня зовут Дмитрий, я написал на C# свой интерпретируемый язык программирования, который назвал — OverScript. Это си-подобный язык со статической типизацией. Сразу скажу, что это не прототип, а готовый проект. Весь код на 100% мой. Я подробно не интересовался, как написаны другие языки, поэтому вся реализация интерпретатора это моя чистая импровизация. Мой подход неконвенциональный, поэтому к техническим аспектам стоит относиться без ассоциаций с тем, что вы могли ранее видеть в других языках, несмотря на то, что некоторые вещи могут казаться знакомыми.


image


Самое важное:


  1. Интерпретатор является полностью независимым, т.е. не использует имеющихся в .NET средств компиляции кода. Он также не использует никаких сторонних библиотек. Это не транслятор, а хардкорный интерпретатор.
  2. OverScript — си-подобный язык с классическими принципами ООП. Он имеет статическую типизацию, поддерживает наследование, виртуальные методы, перегрузку операторов и многое другое. Моя задача была не придумывать новый синтаксис, а сделать язык, привычный для C#-программистов.
  3. Скорость работы сопоставима с Python и ClearScript (проверял на VBScript). OverScript пока чуть уступает, но ещё есть что оптимизировать. При этом он в разы быстрее других интерпретаторов, написанных на C#. Например, в 5 раз быстрее, чем MoonSharp (Lua). C IronPython не сравнивал, т.к. это транслятор компилирующего типа.
  4. OverScript, на мой взгляд, является идеальным языком для встраивания в .NET-программы. Он относительно быстрый и привычный. Да, это не V8, но более удобный язык, чем JS, за счёт, например, той же статической типизации, которая позволяет на стадии загрузки кода выявлять ошибки, связанные с несовместимостью типов.
  5. В OverScript нет проблемы с отсутствием библиотек. Он может использовать типы стандартных .NET-библиотек. Можно импортировать функции из самописных библиотек.

Для работы интерпретатора нужен .NET 6. Можно и под .NET Framework перекомпилировать с небольшими изменениями (не пробовал). Я использовал классический C# без таких нововведений как, например, индексы и диапазоны, которые появились только в версии 8.0.


Пример кода:


string s="Hello, world!"; //это переменная типа System.String
WriteLine(ToUpper(Substring(s, 0, 5))); //HELLO //вызывается базовые функции Substring и ToUpper
WriteLine(s.Substring(0, 5).ToUpper()); //HELLO //то же самое
s.Substring(0, 5).ToUpper().WriteLine(); //а можно и так
WriteLine(s->Substring(0, 5)->ToUpper()); //HELLO //через рефлекшн вызываются стандартные методы типа System.String
//WriteLine - это базовая функция-обёртка для Console.WriteLine(str) 
//В OverScript почти все базовые функции имеют имена .NET-методов, которые они используют

Из простого примера выше видно, что OverScript представляет собой слой абстракции над стандартными .NET-типами. Говоря по-простому, OverScript это программа, которая анализирует код, выстраивает из него определённую структуру из последовательностей отдельных операций, после чего рекурсивно выполняет их.


Ещё пример:


Point[] arr = new Point[]{new Point(25, 77), new Point(122, 219)}; //создание массива из двух экземпляров класса Point
int n; // имеет значение 0 по умолчанию
foreach(Point p in arr){ // перебор всех элементов массива
    n++;
    WriteLine($"{n}) {p.X}; {p.Y}"); // вывод значений с помощью интерполяции строк
}
//1) 25; 77
//2) 122; 219
ReadKey();

class Point{
    public int X, Y;
    New(int x, int y){ // конструктор
        X=x;
        Y=y;
    }
}

Ещё:


const string label = "  O  v  e  r  S  c  r  i  p  t  ";
const int a = '0', z = '9';
const int labelX = 9, labelY = 8, labelX2 = labelX+Length(label);
const object Console = typeof("System.Console, System.Console");
const object DarkGreen = "DarkGreen".ToEnum("System.ConsoleColor, System.Console");
const object Cyan = "Cyan".ToEnum("System.ConsoleColor, System.Console");
bool w, p;
int x, y;
char c;

ReadKey("Press any key to continue");
ClearConsole();
Console->ForegroundColor = Cyan;
string welcome="Welcome to OverScript!";
foreach(c in welcome){Write(c); Sleep(Rand(0, 200));}
Sleep(500);
SetCursorVisible(false);
Console->ForegroundColor = DarkGreen;
foreach(int i in Range(10000)){
    x = Rand(0, 50);
    y = Rand(0, 16);
    p = w;
    w = y == labelY && x >= labelX && x < labelX2;
    c = w ? label[x - labelX] : char\Rand(a, z);
    if(w != p) 
        Console->ForegroundColor = w ? Cyan : DarkGreen;
    SetCursorLeft(x);
    SetCursorTop(y);
    Write(c);
}
Sleep(5000);

Результат выполнения — GIF.


Примеры полезных приложений


  1. Парсер урлов изображений с сайта;
  2. Переводчик текстов через Yandex Translate;
  3. Игра Змейка с использованием GTK#.

Есть много разных языков...


Я написал большой текст о инди-языках и их создании, но решил не вставлять его (слишком занудный), а просто обозначить основные моменты. Когда я начал свой проект, стал искать, что пишут другие, и обнаружил, что любительских проектов просто туча.


  1. Обычно пишут трансляторы в Си, JS, или под LLVM-компиляторы. Чаще всего это JS-подобные языки (функции — объекты, отсутствие полноценного ООП и т.п.).
  2. Используют готовые лексеры, парсеры и прочие инструменты.
  3. Дальше прототипов дело редко идёт. Скорее всего, причина в том, что пишут по шаблону и упираются в фундаментальные ограничения, которые не знают как обойти.
  4. Пишут обычно на C/C++, а C# считается непригодным для таких задач, и проектов почти нет.
  5. Сами языки довольно минималистичны и не про ООП. Дело тут, скорее, не в моде, а в "и так пойдёт".

Тема создания языков с одной стороны популярная, но с другой — маргинальная. Есть известные языки с большими комьюнити, и в новых языках особой необходимости нет. Поэтому новыми проектами, как правило, занимаются любители со специфическим пониманием практической стороны вопроса и стремлением уйти от самостоятельной разработки ядра. Не то чтобы это было плохо, просто толку от этого мало. Почти весь материал (обсуждения, проекты), что я нашёл, по сути — переливание из пустого в порожнее. И как следствие, отношение людей к теме создания новых языков довольно скептическое. Я противник хейта, респект всем, кто пытается сделать что-то новое, но просто констатирую факт.
С новыми языками есть ещё такая проблема: одни ожидают от них привычных возможностей, а другие чего-то революционного. И, задавая вопрос "а зачем он нужен?", сразу готовы ответить "ничего не понятно" или "ничего нового". Это такая почти философская проблема прогресса вообще.
Если трезво смотреть на ситуацию с языками, то единственное, что может дать преимущество новому языку — это лучшая производительность, которая сейчас, на сколько я понимаю, упирается в фундаментальные факторы, связанные с ОС и процессорами. Интерпретаторы же про удобство, доступность и скорость разработки. Ruby, Python или Lua — дело вкуса и привычки. Для меня они непривычны из-за ярко выраженной скриптовости, которая, с одной стороны, сделала их популярными у начинающих, а с другой — обособила не в пользу широкого применения в программах со сложной логикой. OverScript — интерпретируемый язык, но, скажем так, более мейнстримный. Я не противопоставляю его другим, ведь главное в нём даже не статическая типизация, а платформа .NET.
У меня были большие сомнения, что с OverScript получится что-то более-менее приемлемое из-за мифа, что управляемый код не годится для создания интерпретаторов. Но я сразу решил, что цель-минимум — разобраться самому, как устроено программирование со стороны разработчика языка, ведь когда пишешь тривиальные программы на высокоуровневых языках, то кажется, что знаешь всё, но это как с айсбергом — видишь только верхушку. Поэтому я даже не думал о том, чтобы пойти по накатанной дорожке с использованием готовых инструментов. Сейчас уже я уверен, что всё сделал правильно, а C# показал себя отличным языком даже для такой нестандартной задачи, как написание интерпретатора.


Отдельно скажу про вообще все языки. Языков много, но большинство из них либо узкоспециализированные, либо давно устарели. Есть новые языки общего назначения вроде Go и Julia, но я пока не вижу, чтобы они пользовались большой популярностью (в рейтингах цифры весьма скромные). Могу отметить только набирающий обороты Rust, который рассматривается как альтернатива C++, и объединяет сейчас вокруг самых дотошных кодеров, которые за абстракции с нулевой стоимостью и прочие штуки, до которых большинству программистов дела нет. Но Rust сложный, поэтому популярным у широких масс не будет. Так что, думаю, основными языками для любительских и промышленных целей ещё долго будут C# и Java. Можно, конечно, и Python ещё назвать, но мнения о его практическом использовании в сложных проектах слишком полярные (собственной оценки давать не буду, т.к. не писал ничего серьёзного на нём).


Я это всё пишу, чтобы у читателя было какое-то представление о том, как я вижу ситуацию, понимаю сложности, и почему решился написать свой язык. Если подытожить, то кажется, что языков много, но на деле выбор небольшой, особенно, если говорить о языках для встраивания в .NET-программы. Конечно, можно обходиться ClearScript, но JScript/VBScript не те языки, которые можно назвать удобными, если вы привыкли к C#. То же самое касается IronRuby и IronPython. Есть много вариантов скриптинга, но все со своими подводными камнями. Это целая отдельная тема, в которой ключевое значение имеет степень интеграции, и конструктивно обсуждать её здесь из-за принципиальных различий подходов, наверное, не имеет смысла. Это также тесно связано с темой "интерпретаторы vs компиляторы", которая для меня — всё равно что сравнивать устройство электромобилей и бензиновых машин.


Под капотом OverScript


Довольно сложно простыми словами объяснить, как работает интерпретатор OverScript, но я попробую:


  1. Код подготавливается (удаляются комментарии, отступы и лишние пробелы, находятся литералы и т.п.), после чего разбивается на классы и функции.
  2. Код функций разбивается на отдельные логические строки, определяется вид инструкций (if, goto, return и т.д.).
  3. Для управляющих конструкций высчитываются переходы. Например, для if ищется, куда переходить в случае выполнения условия и куда в противном случае.
  4. Из каждой строки рекурсивно выстраивается дерево операций. В итоге имеем набор отдельных элементов: присваивание, переменная, элемент массива, литерал, функция и т.д.
  5. Далее запускается цепочка рекурсивных вычислений. Начинается она от создания главного класса приложения (в других языках обычно вызывается main). Самым первым вызывается метод Instance(), потом конструктор New().

Итого имеем следующие ключевые элементы: класс, функция, логические строки, единицы вычисления (эвал-юниты).


У тех, кто уже изучал устройство других интерпретаторов, наверняка возникнет много вопросов, почему я сделал что-то именно так, а не иначе. Но я не смотрел, как сделаны другие языки, поэтому ожидать какого-то соответствия принятым в них нормам не стоит. Я писал, придумывая на ходу. Возможно, это не лучший подход, но копирование других проектов мне не интересно. К тому же, даже частичное копирование не имеет смысла, если вы не хотите ограничиваться функционалом источника. Как показывает опыт, в какой-то момент придётся всё переписывать с нуля, чтобы иметь возможность добавлять свои нестандартные фичи, которые выходят за рамки заложенного потенциала. И я уж не говорю про то, что разобраться в чужом коде, обвешенном всевозможными молдингами, бывает сложнее, чем написать свой.
Подробно расписывать здесь технические детали не вижу смысла. Скажу только про то, что у каждой операции есть свой тип результата, и все вычисления происходят в обобщённых методах и классах с этим типом. Это про внутренние алгоритмы интерпретатора, а не про сам язык. Каждый раз на точках входа в обобщённые части приходится делать switch по типам, либо вызывать делегаты, что значительно снижает быстродействие. Но пока я не придумал, как обойтись без этого. Я перепробовал много решений (абстрактные классы, интерфейсы), но по сути всегда получаются непрямые вызовы методов, которые снижают быстродействие. Та архитектура, на которой я остановился, на первый взгляд, может показаться неоптимальной, но я пришёл к ней по результатам многих тестов.
И ещё важный момент: в интерпретаторе не используется unsafe-код.


О типах


С простыми типами (int, string, bool и т.д.) всё как в C#. Но, чтобы не было путаницы, в OverScript тип Single называется float (есть функция ToFloat(), а не ToSingle()). Ещё DateTime я назвал date. Нет sbyte, ushort, uint, ulong.
Теперь давайте посмотрим на оператор typeof:


WriteLine(typeof(float)); //System.Single //тут всё понятно
WriteLine(typeof("System.Drawing.Point, System.Drawing")); //System.Drawing.Point //а это, наверное, выглядит странно  

OverScript ничего не знает о .NET-типе System.Drawing.Point. Если написать typeof(System.Drawing.Point), то интерпретатор будет искать ваш собственный класс, а не тип в библиотеке .NET.
Работа с типами, которых нет в OverScript, возможна через рефлекшн:


object Point = typeof("System.Drawing.Point, System.Drawing");
object point = Create(Point, 150, 225); //можно так: Point.Create(150, 225)
WriteLine(point->X + "; " + point->Y); //150; 225

В этом примере, для создания объекта типа Point, используется базовая функция Create, которой передаются тип и аргументы для конструктора (150 и 225). Стрелка (->) в данном случае — это вызов базовой функции GetMemberValue, которая через рефлекшн получает значение свойства/поля. При загрузке кода последняя строка превращается в:


WriteLine(@GetMemberValue(point,"X")+"; "+@GetMemberValue(point,"Y")); 

Символ @ перед именем функций указывает, что нужно вызывать именно базовую функцию без поиска пользовательской.
Сразу нужно запомнить, что typeof срабатывает на этапе загрузки кода, и заменяется в коде на литерал (ссылку на объект). Получить тип во время выполнения можно функцией GetTypeByName. В большинстве случаев можно писать как typeof(int), так и просто int (по сути, это как константа).


Получаем курсы валют


Теперь разберём более сложный пример, в котором с сайта www.cbr-xml-daily.ru загружаются курсы валют в формате JSON, данные десериализируются и выводятся построчно:


object JsonDocument=typeof("System.Text.Json.JsonDocument, System.Text.Json"); //получаем тип JsonDocument, который будем использовать для парсинга JSON данных
WriteLine("Загрузка курсов валют..."); //выводим строку, что начинается загрузка данных с сайта
string json=Fetch("https://www.cbr-xml-daily.ru/daily_json.js"); //get-запросом получаем ответ сервера с курсами валют в JSON формате
int i=json.IndexOf("\r\n\r\n"); //ищем два переноса строки, чтобы удалить http-заголовки. json.IndexOf("\r\n\r\n") - это вызов базовой функции IndexOf(json, "\r\n\r\n")
if(i<0 || json.IndexOf(" 200 OK")<0){ //если переносы не найдены, либо в http-ответе нет кода 200 OK
    WriteLine("Не удалось загрузить данные!"); //выводим сообщение об ошибке
    ReadKey("Нажмите любую клавишу для выхода"); //выводим текст, и начитается ожидание нажатия  (а ReadKey() в C# не умеет выводить сообщение)
    return; //после нажатия любой клавиши произойдёт завершение работы программы
}
json=json.Substring(i+4); //из http-ответа берём только тело, без заголовка. json.Substring(i+4) - это Substring(json, i+4).
//далее нужно прочитать данные из JSON
object defaultJsonDocumentOptions=Create("System.Text.Json.JsonDocumentOptions, System.Text.Json"); //создаём объект JsonDocumentOptions, который дальше нужно будет передать методу Parse 
object docRoot=JsonDocument->Parse(json, defaultJsonDocumentOptions)->RootElement; //сначала статическим методом Parse класса JsonDocument получаем из JSON-строки объект со структурированными данными, а потом из этого объекта получаем корневой элемент
WriteLine("Курсы валют на: "+docRoot->GetProperty("Date")); //из корневого элемента получаем дату обновления данных. GetProperty - это метод структуры System.Text.Json.JsonElement.
/*
теперь взглянем на то, как в JSON хранятся курсы валют:

"AUD": {
    "ID": "R01010",
    ...
},
"AZN": {
    "ID": "R01020A",
    ...
},
"GBP": {
    "ID": "R01035",
    ...
}...

Это перечисление объектов. Далее нужно перебирать и десериализовывать их по одному.
*/
object rates=docRoot->GetProperty("Valute")->EnumerateObject(); //получаем перечислитель объектов c данными по каждой валюте. EnumerateObject - это метод структуры System.Text.Json.JsonElement.
foreach(object item in rates){ //перебираем объекты так же, как в C#.
    object val=item->Value; //получаем объект System.Text.Json.JsonElement. Это данные о конкретной валюте. Например:
    /*
    "ID": "R01010",
    "NumCode": "036",
    "CharCode": "AUD",
    "Nominal": 1,
    "Name": "Австралийский доллар",
    "Value": 54.0507,
    "Previous": 54.137
    */
    Valute v=FromJson(val.ToString(), Valute); //десериализуем в объект типа Valute, класс которого прописан в конце программы. FromJson - встроенная функция, которой передаётся json-текст и тип, в экземпляр которого нужно его превратить. val.ToString() - это базовая ToString(val).
    //теперь у нас есть экземпляр нашего класса Valute, в котором каждой переменной присвоено соответствующее значение из JSON-структуры, и мы можем просто вывести нужные нам данные
    WriteLine($"{v.Nominal} {v.Name.ToLowerFirst()} ({v.CharCode}): {v.Value}"); // выводим через интерполяцию строк номанал валюты, её название и сколько рублей она стоит. v.Name.ToLowerFirst() - это ToLowerFirst(v.Name).
}
/*Результат:
Курсы валют на: 2021-08-07T11:30:00+03:00
1 австралийский доллар (AUD): 54,0507
1 азербайджанский манат (AZN): 43,0432
1 фунт стерлингов Соединенного королевства (GBP): 101,7683
100 армянских драмов (AMD): 14,8383
...
*/
ReadKey(); //ожидает нажатия любой клавиши, после чего программа закрывается

class Valute{ //этот класс повторяет тип данных валюты в JSON-е
    public string ID;
    public string NumCode;
    public string CharCode;
    public int Nominal;
    public string Name;
    public decimal Value;
    public decimal Previous;
}

Как видим, структурно код такой же, как в C#. Понимаю, что object-переменные выглядят непривычно, но в целом, думаю, код понятен. Можно использовать подсказки типа, которые делаются из констант типа при помощи машинописного обратного апострофа:


const object Point=typeof("System.Drawing.Point, System.Drawing");
`Point p=Point.Create(10, 20); //интерпретатор знает, что в p должен находиться объект типа Point
WriteLine(p->X); //p->X - это @TGetValue(#Int32 X#,int,p), где #Int32 X# - объект MemberInfo 

В этом примере p — это object-переменная с подсказкой, что в ней находится экземпляр System.Drawing.Point. Это позволяет ускорить обращение к членам объекта, т.к. интерпретатор будет искать члены (не значения, а MemberInfo) не во время выполнения, а один раз при загрузке кода. Также вы не сможете обычным способом присвоить такой переменной значение неподходящего типа (по ошибке).


Покажу пару фич:
1) Кроме привычного try/catch есть очень простой способ перехвата ошибок при вызове функций:


string s="test";
WriteLine(s.Substring(9)("error message")); //error message
if(exception!=null) WriteLine("Error: "+exName+" ("+exMessage+")"); //Error: ArgumentOutOfRangeException (startIndex cannot be larger than length of string. (Parameter 'startIndex'))
//exception, exName и exMessage - специальные переменные, в которые записывается информация об исключении

Значение во вторых скобках возвращается в случае, если функция выбросила исключение.
Операторы — это функции, поэтому с ними тоже так можно:


WriteLine((5/0)(123)); //123

2) Выражения можно передавать и выполнять как объекты.


Go();

Go(){
    int x, y;
    object e=Expr(x+y);
    Test(e);
}

Test(object e){
    int x=2, y=3;
    WriteLine(e.Eval(int)); //5
}

Ремонт невозможно закончить — его можно только прекратить


Начал я этот проект в середине декабря 2020-го. Более-менее рабочий прототип был готов, как мне казалось, уже через месяц. Дальше я доделывал и переделывал, и по мере добавления функционала, становилось понятно, что нужно усложнять общую архитектуру. Правка старого кода приводила к его полному переписыванию. У меня было желание выложить всё уже через 2-3 месяца, но постоянно находилось что-то, что требует обязательной доработки.
Надо сказать, что психологически довольно непросто работать долго без фидбэка, особенно если ты пишешь что-то необычное, и непонятно, будет ли это вообще кому-то интересно. Несмотря на то, что ещё много чего нужно доделывать, я решил, что пора уже выложить то, что есть, чтобы понять по отзывам, что нужно сделать/переделать в первую очередь. Я отложил некоторые решения потому, что не уверен в их уместности. Например, нужны мнения о том, какие нестандартные перегрузки операторов добавить. В Python можно "abc"*3 и получить "abcabcabc". Удобно, но вдруг программист по ошибке пытается умножить строку на число?.. Где грань между допустимыми поблажками и фичами, от которых больше вреда, чем пользы?.. Чтобы избежать холивара, предлагаю обсуждать это исключительно в контексте интерпретаторов, для производительности которых важна лаконичность кода (максимальная автоматизация).


Сейчас в OverScript есть только самое основное. Нет многомерных массивов, интерфейсов, дженериков, лямбд, struct-ов, checked/unchecked и много чего ещё. Что-то добавить легко, что-то сложно, а что-то просто не нужно, ведь OverScript простой интерпретируемый язык, и нецелесообразно добавлять в него всё, что есть в C#.
OverScript ещё тестировать и тестировать. Я постоянно нахожу новые ошибки и код, который можно улучшить. И я практически не оптимизировал загрузку кода, только выполнение. В будущем я планирую сделать кэширование загрузки скрипта, чтобы ускорить повторные запуски.


Итог


В целом, я доволен результатом, но чувства неоднозначные. Во-первых, получилось лучше, чем ожидал, но многие задуманные фичи пока не реализованы. А во-вторых, у меня сейчас что-то вроде синдрома самозванца. Мой код, мягко говоря, неидеален, местами сумбурный, есть временные решения. И кто-то может возмутиться, что вот с этим я чуть ли не на лавры великого Питона покусился. Но, как я уже писал, ниша OverScript — это .NET. Есть Iron-языки, но это вторичные решения со своими легаси-особенностями. OverScript же свободен и лёгок на подъём! И как гласит китайская поговорка (тут представляем известный мем с китайским мудрецом): Увидеть лучше, чем услышать, познать лучше, чем увидеть, сделать лучше, чем познать.


Если вам интересен мой проект, то вы можете поддержать его донатом. Разработка заняла немало времени, и на доработку, скорее всего, уйдёт не меньше. Свои некоммерческие проекты — это здорово, но нужно и деньги как-то зарабатывать. Сейчас развитие проекта под угрозой, т.к. из-за сами знаете какой ситуации в России, где я нахожусь, я лишился всех источников дохода, и ваши донаты для меня сейчас единственная возможность продолжить активную работу над проектом.


Если у вас есть вопросы, интересные идеи или даже коммерческие предложения — пишите на support@overscript.org.


Спасибо за внимание, и с нетерпением жду ваших комментариев. Фух… Хей, хоу, летс гоу!

Комментарии (84)


  1. dopusteam
    02.04.2022 20:56
    +28

    А можете добавить раздел "Для чего это?", а то непонятно зачем, если уже есть c#

    Сразу скажу, что это не прототип, а готовый проект

    OverScript ещё тестировать и тестировать. Я постоянно нахожу новые ошибки и код, который можно улучшить


    1. xeleos
      02.04.2022 21:07
      +1

      вероятно, главное отличие от c# то, что он интерпретируемый и его можно запускать сразу после редактирования скрипта. как с питоном\пхп\жс.


      1. FuncBall Автор
        02.04.2022 21:11

        в общем, так и есть.


      1. vabka
        02.04.2022 21:19
        +12

        У C# есть интерактивный вариант — C# Interactive. Запускается через csi.exe
        Используется компиляция на лету.


        Если хочется что-то более менее стабильное и встраиваемое — есть Mond, Lua, ещё Lua. И это только то, что нашёл сравнительно быстро.
        Можно вообще взять Roslyn и просто брать и выполнять C#. В чём смысл ещё одного языка?


        1. FuncBall Автор
          02.04.2022 21:29

          если на то пошло, то можно вообще код прямо из C# на лету компилить.
          OScript видел, но это что-то совсем узкоспециализированное.


          1. fedorro
            03.04.2022 11:38

            если на то пошло, то можно вообще код прямо из C# на лету компилить.

            О том и речь - для этого не нужно ни зависимостей, ни тонны кода, не нужно учить отличия в синтаксисе, производительность не уступает оригинальному коду.

            Но как личный проект - круто, конечно ????


            1. FuncBall Автор
              03.04.2022 12:23

              Спасибо!
              Компилирование средствами .NET - отдельная тема. Когда код компилируется, он становится частью монолита с жесткими привязками. Плюс - скорость, минус - такая степень интеграции не всегда уместна. Т.е. когда вы переносите исполнение на слой CLR, то теряете некоторую гибкость контроля. Например, зациклится что-то, и вам нужно будет это как-то жестко останавливать.


              1. fedorro
                03.04.2022 12:38

                Не становится он частью монолита - просто байтики в памяти,которые можно как сбору сохранить или выполнить на месте.

                Большая степень интеграции - это не минус, просто не используйте те части, что не нужны. Зато когда будут нужны - они есть.

                Если что-то зациклится - убиваете поток, и всё. Не понятно что под жесткой и под мягкой остановкой подразумевается, возможно упустил момент из статьи. В любом случае если повисло - значит с кодом что-то не то.


                1. FuncBall Автор
                  03.04.2022 13:00

                  жестко - убиваете поток. Эх, жалко, что Thread.Abort убрали.


                  1. shai_hulud
                    03.04.2022 13:07
                    +1

                    Ох, не жалко. Создание механизма который позволяет сломать случайное состояние программы было ошибкой, то что они его убрали это шаг в правильном направлении.


                    1. FuncBall Автор
                      03.04.2022 13:18

                      Понимаю, но можно было бы сделать скрытую настройку для включения на свой страх и риск.

                      А вы как, через Thread.Interrupt имеете в виду убивать?


                      1. shai_hulud
                        03.04.2022 14:28
                        +1

                        Убивать можно через управление рантаймом, к примеру интерпретатором. Вручную через CancellationToken. Через инструментацию кода и добавления точек прерывания на вызовы IO, в тело циклов и вызовов суб-рутин. Нормальных вариантов много, Thread.Abort это не выход.

                        Понимаю, но можно было бы сделать скрытую настройку для включения на свой страх и риск.

                        Есть winapi и TerminateThread для особо "смелых".


                1. KvanTTT
                  03.04.2022 15:35
                  +1

                  Просто и правильно можно убить только процесс, поток убивается с помощью Thread.Abort, который задепрекейтили, ну либо с помощью CancellationToken, но это нужно дополнительно код фиксить, что не всегда уместно.


                  1. FuncBall Автор
                    03.04.2022 16:31

                    всё так. Например, в случаях, когда пользователям разрешается расширять функционал какой-то системы скриптами, нет никакой гарантии, что они будут в алгоритмах предусматривать мягкую остановку. И Abort был грубым, но решением.


                    1. lair
                      03.04.2022 20:06

                      И Abort был грубым, но решением.

                      Проблема в том, что это решение (а) временное (в .Net 5 уже не работает), и (б) нестабильное, потому что вы не знаете, что с вашим доменом после этого.


    1. FuncBall Автор
      03.04.2022 11:01
      +1

      Прототип - это версия-макет, в которой отсутствует часть базового функционала (стоят заглушки). Готовый проект - тот, в котором всё работает, но в нём могут быть ошибки.


      1. dopusteam
        03.04.2022 11:59

        Поделитесь источником определений


        1. FuncBall Автор
          03.04.2022 12:30

          1. dopusteam
            03.04.2022 12:38

            Выглядит как жонглирование терминами. Вы уверены что вышли из стадии прототипа, если там ещё "тестировать и тестировать"?


            1. FuncBall Автор
              03.04.2022 13:50

              Тестирование же подразумевается не только до релиза, но и после. Это обычное дело. Посмотрите, сколько фиксов в репозиториях известных языков.


              1. dopusteam
                03.04.2022 14:07

                Одно дело - поддержка продукта и фиксы багов, и другое - продукт готовый, но там "ещё тестировать и тестировать, постоянно нахожу баги"

                Стали бы пользоваться условным .net если бы разработчики сказали, что его ещё тестировать и тестировать?


  1. FuncBall Автор
    02.04.2022 21:03
    +2

    Увидеть лучше, чем услышать, познать лучше, чем увидеть, сделать лучше, чем познать ;)


  1. Tzimie
    02.04.2022 21:13
    +3

    Жениться вам, барин, надо

    Начал я этот проект в середине декабря 2020-го. Более-менее рабочий прототип был готов, как мне казалось, уже через месяц. Дальше я доделывал и переделывал, и по мере добавления функционала, становилось понятно, что нужно усложнять общую архитектуру. Правка старого кода приводила к его полному переписыванию. У меня было желание выложить всё уже через 2-3 месяца, но постоянно находилось что-то, что требует обязательной доработки.


    1. FuncBall Автор
      02.04.2022 21:19
      +5

      сударь, что вы всё намёками да намёками?..


      1. aabdullin
        04.04.2022 08:34

        Вам тридцать, а вы всё ещё в дамках, как так?..


  1. addewyd
    02.04.2022 21:34
    +2

    А почему так много пустых строк в исходниках?


    1. FuncBall Автор
      02.04.2022 22:30

      много где был закомментированный старый код, после удаления остались пустые строки.


  1. Makeman
    02.04.2022 22:28

    Понимаю, что object-переменные выглядят непривычно, но в целом, думаю, код понятен.

    Сразу возникает мысль поменять их на var ;-)

    Респект за труд! Правда, такие проекты, скорее, для саморазвития, чем для широкого применения.

    Если вдруг кто-то не знаком, есть классный онлайн-сервис от команды разработки C# - SharpLab.io. Позволяет прямо в браузере писать программы/скрипты, на лету их компилировать и выполнять. Очень удобный инструмент, чтобы оформить какой-то небольшой код и поделиться им с другими людьми.

    Ещё можно поучаствовать в обсуждении новых фич языка или даже предложить свои Issues · dotnet/csharplang (github.com).


    1. FuncBall Автор
      02.04.2022 22:40

      можно писать var.


    1. vabka
      04.04.2022 17:43

      есть классный онлайн-сервис от команды разработки C# — SharpLab.io

      А откуда такая информация? Просто в профиле автора (Andrey Shchekin) ничего о месте работы не сказано.
      Да и каких-то коммитов в language design или .net у него нет.


    1. KvanTTT
      04.04.2022 18:11

      Если вдруг кто-то не знаком, есть классный онлайн-сервис от команды разработки C# — SharpLab.io. Позволяет прямо в браузере писать программы/скрипты, на лету их компилировать и выполнять.

      C# песочниц и без этого сервиса полно. А SharpLab ценный тем, что позволяет отображать IL и даже ассемблер для фрагмента кода на C#, что удобно для оценки оптимизаций и понимания работы компилятора.


  1. ap1973
    02.04.2022 22:31
    +3

    А я вот поддержу автора.

    1. Не так много встраиваемых, статически типизированных языков, и для определенной ниши - крайне полезно;

    2. Это просто клевый Just for fun - я завидую!


    1. ap1973
      02.04.2022 22:42

      Ну уж коль тут такая пляска, вопросы (скорее один вопрос):

      Я в свое время, лет 10 назад, пробовал использовать динамическую компиляцию в C#, уперлось все в то, что динамические сборки не выгружались из памяти и плодили версии самих себя. В .Net core, как я понимаю, все уже по другому, есть возможность динамически собирать что-то (сборку, или модуль...)? В Roslyn это было как-то не очень, но я смотрел еще на очень ранние варианты

      P.S. Я сейчас не в мире .net, извините за возможно глупые, устаревшие вопросы


      1. FuncBall Автор
        02.04.2022 22:51

        ну CSharpCompilation.Create. Про "не выгружались из памяти" не знаю, я глубоко не погружался.


    1. FuncBall Автор
      02.04.2022 22:43

      очень-очень рад :)


  1. realchel
    02.04.2022 22:57
    +1

    программист-програматик читали? нет прочтите.


    1. ap1973
      02.04.2022 23:25

      Хорошая книга, если вы про Ханта. Но не очень понял, как ваш комментарий соотносится с текстом статьи. Правда интересно, разверните мысль.


      1. realchel
        03.04.2022 11:16

        На сколько я помню, там была одна рекомендация писать свой "язык", если он нужен для предметной области.


  1. zorn_v
    02.04.2022 23:38

    Привет! Меня зовут Дмитрий, я написал на C# свой интерпретируемый язык программирования, который назвал — OverScript.

    А к0р0ваны можно грабить ? )

    Ну зачем ? Вобщем и до меня уже сказали наверное.


    1. FuncBall Автор
      03.04.2022 11:26

      Корованы не можно, а нужно!

      Вам интересна тема встраиваемых языков для .NET?

      Я ожидал, что вопрос "Зачем?" будет самым популярным. Т.к. вопрос задан в краткой форме, то отвечу двусмысленным вопросом, который на самом деле не вопрос: Всегда ли выбор программистами инструментов рационален? Посмотрите с разных сторон.


  1. lair
    03.04.2022 01:25
    +2

    я написал на C# свой интерпретируемый язык программирования, который назвал — OverScript.

    А зачем? Какую конкретную задачу этот язык решает?


    Я подробно не интересовался, как написаны другие языки, поэтому вся реализация интерпретатора это моя чистая импровизация.

    Но почему?


    1. AirLight
      03.04.2022 02:16
      +3

      Месье практик, а не теоретик. Они всегда так делают. На вопрос "почему" здесь скорее может ответить эволюция, которая раздает подобные свойства отдельным людям.


      1. lair
        03.04.2022 02:18

        Месье практик, а не теоретик. Они всегда так делают.

        Кто "они"?


        1. VXP
          03.04.2022 03:58

          Практики.


          1. lair
            03.04.2022 04:14

            Странно. Я вот практик, я так не делаю. Как раз наоборот, я считаю практичным сначала разобраться, что делали другие в данной области.


            1. Devoter
              03.04.2022 05:00
              +2

              Вы, по всей видимости, другого племени практик, такой практик, который сперва ищет и изучает имеющиеся аналоги, вникает в домен, изучает теоретическую базу при необходимости и только потом решает: стоит ли писать свое.

              Я вот тоже тот ещё любитель велосипедов, но отвечать, хотя бы самому себе, на вопрос "зачем" определенно стоит.


              1. lair
                03.04.2022 05:30
                +1

                Тогда несколько странно слышать "они [практики] всегда так делают", если практики могут быть "другого племени".


                Я потому, собственно, и спросил, что подумал, может, неправильно фразу понял, и речь о теоретиках идет на самом деле.


                1. AirLight
                  04.04.2022 01:50

                  Предположу, что это сарказм. Тот кто сперва сначала изучает, сравнивает, проектирует - это определенно уже не практик, поскольку тут приоритет в работе с теоретической информацией. Теоретики тоже делятся на прагматиков и концептуалистов. Но реальные практики не акцентированы вообще на предварительном изучении обобщений. Им вообще обобщениями неудобно пользоваться. Рекомендую поизучать типологии.


                  1. lair
                    04.04.2022 02:08

                    Тот кто сперва сначала изучает, сравнивает, проектирует — это определенно уже не практик, поскольку тут приоритет в работе с теоретической информацией.

                    Нет, не "определенно".


                    Рекомендую поизучать типологии.

                    А где вы взяли общепринятую объективную типологию, можно ссылку?


                1. AirLight
                  04.04.2022 01:53
                  -1

                  Ваше заблуждение в том, что вы зачем-то себя считаете практиком. Если Вы практик, то как тогда выглядят теоретики? И как назвать тех кому не интересно изучать общие правила, а удобнее попробовать самому своими руками?


                  1. lair
                    04.04.2022 02:10

                    Ваше заблуждение в том, что вы зачем-то себя считаете практиком.

                    Это точно не заблуждение. Я больше десяти лет выпускаю разного рода ПО, это точно не теоретические изыскания.


                    Если Вы практик, то как тогда выглядят теоретики?

                    Теоретики — это те, кто рассуждают о коде, но (практически) его не пишут. CS, а не SE.


                    И как назвать тех кому не интересно изучать общие правила, а удобнее попробовать самому своими руками?

                    Есть хорошее английское слово tinkerer. Русского аналога не знаю.


                  1. FuncBall Автор
                    04.04.2022 12:42

                    Любимая тема у музыкантов. А ещё уж очень вся эта дискуссия по духу похожа на рассуждения о "форме и содержании" (с известной коннотацией).
                    Каждый, в определенной степени, есть и теоретик и практик. Просто одни больше фокусируются на технических нюансах (как работает), а другие на практической реализации (выполняет ли задачу).
                    Не помню кто из известных авторов (то ли Роберт Мартин, то ли Рой Ошероув) говорил, что главное, чтобы программа выполняла свою задачу. Если цель достигнута, то тех. решение верное. Большинство решений - частные. Теория никогда не применяется в чистом виде, т.к. не подразумевает универсальной имплементации. Например, есть "Цепи Маркова", и какие я только вариации/адаптации не видел! Большая их часть, строго говоря, вообще к оригинальной теории никакого отношения не имеет.


                    1. lair
                      04.04.2022 14:04

                      Любимая тема у музыкантов.

                      Мне прямо любопытно стало, какая же тема "любимая" у музыкантов...


                      1. FuncBall Автор
                        04.04.2022 14:15

                        нужно ли знать теорию музыки, чтобы быть крутым музыкантом.


                      1. lair
                        04.04.2022 14:18

                        Ой, это как раз очень простой вопрос, я вам как человек с правом преподавания теории музыки говорю: нет, не обязательно знать теорию музыки, чтобы быть крутым музыкантом. Но если знать теорию музыки, то стать крутым музыкантом легче.


                      1. FuncBall Автор
                        04.04.2022 14:41

                        согласен, но тема опасная, т.к. творческие люди часто очень болезненно реагируют на разные мнения.


  1. rsashka
    03.04.2022 09:54
    +1

    В Python можно "abc"*3 и получить "abcabcabc". Удобно, но вдруг
    программист по ошибке пытается умножить строку на число?.. Где грань
    между допустимыми поблажками и фичами, от которых больше вреда, чем
    пользы?..

    Решил эту дилемму у себя https://habr.com/ru/company/timeweb/blog/651999/ следующим образом: Изначально отказаться от операторов с двойным смыслом (как для умножения строки на число или сложения двух строк). Но так как эти операции востребованы, то заменить их похожими по смыслу операторами "abc" ** 3 -> "abcabcabc" и "abc" ++ "def" -> "abcdef". Таким образом исключается случайное использование неправильного оператора, а заодно и исключаются сложности с операторами инкремента/декремента, которые по большому счет вообще не нужны.


    1. dikey_0ficial
      03.04.2022 12:25
      +1

      так-то ** могут воспринять как возведение в степень)


      1. rsashka
        03.04.2022 13:09

        Это и есть возведение в степень :-) Ведь задача была в том, чтобы разделить оператор обычного умножения и дублирования строки.


    1. FuncBall Автор
      03.04.2022 12:55
      +1

      Посмотрел ваш проект - необычно, но интересно! Буду следить.
      Да, можно добавлять операторы, правда новички будут пугаться разнообразия.


      1. rsashka
        03.04.2022 13:10

        И я тоже на ваш проект закладочку у себя сразу поставил.


  1. Roman-Usaty
    03.04.2022 10:37

    "Охренеть, Чего б..." Было моей первой мыслью когда увидел пост. Никогда бы не подумал что на шарпе можно писать свой собственный ЯП. Черт, это правда круто. Очень. Я почему то всегда думал что интерпретаторы и компиляторы пишутся на чем-то более низкоуровневом (типа си и т.п.) Спасибо что показали другую грань шарпа. Автор молодец:)


    1. FuncBall Автор
      03.04.2022 10:50

      Спасибо! Я тоже так думал. C# недооценен пока.


    1. blood_develop
      03.04.2022 13:24

      [Вот здесь](https://habr.com/ru/post/426961/) немного истории о том, как c# переписывали сам на себя.

      Вообще, для компилируемых языков существует тру-практика, когда компилятор создаваемого языка переписывается на нам же.

      В таком случае возникает вопрос. Если компилятор шарпа написали на шарпе, почему возникла в ваших умах его недооцененность в написании компиляторов для других языков? В чем принципиальная разница?


      1. FuncBall Автор
        03.04.2022 13:38

        Компилятор можно на любом языке написать. На скорость программы это не влияет. А интерпретатор, считается, нужно писать на быстрых языках. Управляемый код требует дополнительных затрат, поэтому он как бы не может быть быстрым. Но, за счёт автоматических оптимизаций во время компилирования в машинный код, он получается быстрым. Я недавно сравнивал скорость простого цикла с Rust, и в C# у меня получилась выше процентов на 20-30. Удивился, я не эксперт в Rust, может чего-то не знаю.


        1. EragonRussia
          03.04.2022 14:31

          В микробенчмаркинге языков очень легко измерить производительность диаметрально противоположных программ.

          Но вообще рискну уточнить, вы --release при запуске бенчмарка на Rust'е использовали?


          1. FuncBall Автор
            03.04.2022 15:11

            скорее всего.


  1. KvanTTT
    03.04.2022 15:41

    Подробно расписывать здесь технические детали не вижу смысла.

    Ну вот это как раз было бы интересно.


    А так вообще писать новый язык для саморазвития приветствуется, но дальше двигать не стоит, особенно если нет многолетнего опыта в компиляторах.


    1. FuncBall Автор
      03.04.2022 17:04

      Это, если всё писать, то целый цикл статей получится.

      Для начала я советую посмотреть эти места:
      Выполнение функции (проход по логическим строкам).
      Выполнение единицы вычисления (эвал-юнита) -
      По сути, любое выражение - это дерево из эвал-юнитов. Метод ExecuteFunction проходит сверху вниз по всем строкам и методом Eval выполняет эвал-юниты, из которых строки состоят, передавая номер области видимости (scope), ссылки на текущий экземпляр (inst) и стек вызовов (cstack).


  1. EvgenyKlimov
    03.04.2022 18:55

    В комментариях очень много вопросов и критических высказываний двух типов:

    "А зачем вообще это нужно?"

    "Зачем интерпретатор C#, если можно быстро скомпилить?" и типа "Скомпилированное лучше"

    Такое ощущение, что на статью обратили внимание многие, кто на определённом уровне знаком с C#, но либо толком не реализовал ни одного достойного внимания проекта, либо относится к кодингу потребительски (использую только то, что сделали другие).

    Расписывать теорию о пользе скриптов я не буду. Об этом достаточно информации в сети (гугл в помощь). Есть те, кто познал/ощутил прелести от использования, и те, кто ещё нет. Я использую скрипты уже лет 20, и не представляю для себя ни одного крупного проекта без встроенной консоли с возможностью кодинга "на лету". Не говоря уже о проектах, в которых интерпретатор является основой выполнения ряда рутин (например, в гейминге, CAD, ИИ, 3D редакторы, и тд, и тп). Я использовал интерпретаторы C, C++, C#, Lua, Python. Точно могу сказать, что жизнь без них была бы серой и скучной.

    Почему C#? Ну тут автор уже не раз ответил: чтобы использовать тот же язык, на котором пишешь. И это правильно. Написал, попробовал, получилось, добавил в основной проект. Преимуществ море. К сожалению, до сего момента я имел опыт только с одним интерпретатором - PaxScript. Других не встречал (хотя искал).

    В общем, автору огромный респект и удачи в развитии проекта!


    1. FuncBall Автор
      03.04.2022 19:06

      Спасибо! А этот PaxScript настоящий интерпретатор? Смахивает на транслятор.


      1. EvgenyKlimov
        03.04.2022 20:22

        Честно говоря, тут получается какая-то тонкая грань, которую я, возможно, не улавливаю (просто не придавал значения). Я не очень глубоко копался, но, насколько помню, там, конечно, нет никакого MSIL, регистров и тд. Он парсит код, создаёт составные объекты - классы, структуры, организовывает списки простейших команд в функции. Но в качестве операторов типов использует встроенные операторы. Подцепляет типа проприетарные, автозагружая из указанных сборок. Но нет реализации интерфейсов (вроде), генериков и естественно нельзя переопределять виртуальные функции, объявленные в объектах из сборок (вирт ф-ции скриптовых классов можно).

        Сделайте рефакторинг для интереса, там почти всё понятно. Я хотел купить полные сорцы, но автор не вышел на связь.


  1. FuncBall Автор
    03.04.2022 19:05

    удалено


  1. maxcat
    03.04.2022 21:39

    Заместительная идея! Но зачем делать отсебятину?

    Прелесть интерпретируемо1 релаизации c# ещё и в том, что можно просто взять и использовать одинаковый код и для интерпретации, и для компиляции.

    Ну то есть проверили в интерпретаторе, поправили пока не будет работать - засунули в компилятор. И в обратную сторону тоже бывает нужно.

    А в вашей реализации так не сделаешь, из-за специфичных "->".

    Сам я не раз хотел замахнуться на свой интерпретатор c#, но как-то ленился. А потом уже и энтузиазм пропал, тем более что я наткнулся на готовый SlowSharp. В котором вроде как всё без отсебятины точно так же, как в c#.

    Кстати, хотелось бы сравнений со SlowSharp, ну там в скорости и совместимостью с обычным c#


    1. FuncBall Автор
      03.04.2022 22:14

      Про SlowSharp первый раз слышу. Открыл исходники и вижу:

      using Microsoft.CodeAnalysis;
      using Microsoft.CodeAnalysis.CSharp;
      using Microsoft.CodeAnalysis.CSharp.Syntax;

      Это значит, что разбор кода делается стандартными средствами .NET. Таких "языков" сотни. Если вам синтаксис C# нужен, то и ок, наверно.


      1. maxcat
        04.04.2022 00:18

        С синтаксисом Шарпа таких языков не сотни. А только SlowSharp.

        Так а что плохого в разборе кода стандартными средствами? Ну то есть это может быть плохо, когда целевая платформа не содержит их по каким-то причинам. Но в случае SlowSharp всё целивые платформы их содержат, ведь он работает под unity даже на IOS, UWP, WebGL

        Или вы имеете ввиду, что вам хотелось в любом случае сделать именно свой анализ кода?


        1. FuncBall Автор
          04.04.2022 11:05

          Так а что плохого в разборе кода стандартными средствами?

          Ничего плохого. Работает, устраивает - замечательно. В SlowSharp, как я понял, в основном самопис, а значит автору в любом случае респект.

          Или вы имеете ввиду, что вам хотелось в любом случае сделать именно свой анализ кода?

          Знаете как барабанщики относятся к драм-машинам? "Голый барабанщик" смотрели? Одна из моих любимых комедий.

          С синтаксисом Шарпа таких языков не сотни. А только SlowSharp.

          Когда я искал, что пишут люди, то нашёл с десяток проектов под заголовками в духе "пацаны, зацените курсач". К сожалению, ссылки не сохранил. Я удивился, как студенты пишут такие сложные проекты, но потом увидел, что это все через стандартные средства делается. Даже упоминания о методичках встречал, т.е. где-то это на поток поставлено. И люди, когда слышат "я написал свой язык", часто воспринимают это несерьезно потому, что они это много раз уже слышали.
          Когда-то давно-давно вышел музыкальный редактор eJay, все бросились создавать треки (и я тоже) на стандартных сэмплах. Получались однотипные поделки, и с тех пор многие воспринимают электронную музыку как конструктор Лего.


    1. maxcat
      04.04.2022 00:15

      Замечательная идея*


  1. Ma0oo
    03.04.2022 21:51

    Надеюсь этот язык найдет применение в юнити, а то постоянные перекомпиляции проекта немного уже надоели.


    1. FuncBall Автор
      03.04.2022 22:21

      надо распространить пост среди юнити-разработчиков. Можете мне в личку написать, какие сейчас активные комьюнити есть?


      1. maxcat
        04.04.2022 00:49

        Один из самый действенных способов это выложить плагин в ассетсторе


    1. maxcat
      04.04.2022 00:21

      UniScript(SlowSharp) как раз для юнити сделан.

      Ну а постоянные перекомпиляции проекта в юнити надоедают потому что юнитеки сборку не редко делают специально неправильно, ну и ещё случайно плодят баги


  1. gochaorg
    05.04.2022 00:20

    Есть вопрос о реализации (возможно я не всю документацию посмотрел)

    • Есть документ по грамматике языка ? (БНФ или еще какая грамматика)

    • А зачем @ при вызове функции ? что бы найти целевую функцию ?

    • О работе typeof() - Как я понимаю тип переменной определяется в runtime - путем дополнительной ссылки на тип ? как вы выделяете свой тип (класс) от встроенных классов ?

    • Есть ли механизм для debug ?

    • Смотрели вы или нет протокол Language Server Protocol ?


  1. FuncBall Автор
    05.04.2022 12:55

    Есть документ по грамматике языка ? (БНФ или еще какая грамматика)

    Нет.

    А зачем @ при вызове функции ? что бы найти целевую функцию ?

    С @ будет вызываться базовая функция без поиска перегрузки в коде. Вот пример:

    WriteLine("test"); //вызов своей функции
    void WriteLine(string str){
    	//а теперь вызываем базовую функцию:
    	@WriteLine("str: "+str); //str: test //без @ произойдёт зацикливание
    }

    О работе typeof() - Как я понимаю тип переменной определяется в runtime - путем дополнительной ссылки на тип ? как вы выделяете свой тип (класс) от встроенных классов ?

    typeof при загрузке, а не при выполнении. Про доп. ссылку не понял. Свой тип без кавычек.

    Есть ли механизм для debug ?

    Пока нет.

    Смотрели вы или нет протокол Language Server Protocol ?

    Читал описание, но в детали не вдавался. Надо будет разобраться.

    Хотите написать IDE или подключить к существующей?