Есть ли способ реализовать пользовательские функции языка в C #?

c# syntactic-sugar language-construct

8784 просмотра

6 ответа

2692 Репутация автора

Некоторое время я ломал голову над этим и немного огляделся, не найдя ни одной дискуссии на эту тему.

Предположим, я хотел реализовать тривиальный пример, например, новую циклическую конструкцию: do..until

Написано очень похоже сделать .. в то время

do {
    //Things happen here
} until (i == 15)

Это может быть преобразовано в действительный csharp, сделав так:

do {
    //Things happen here
} while (!(i == 15))

Это, очевидно, простой пример, но есть ли способ добавить что-то подобное? Идеально в качестве расширения Visual Studio для включения подсветки синтаксиса и т. Д.

Автор: Thebigcheeze Источник Размещён: 31.07.2012 09:24

Ответы (6)


0 плюса

56588 Репутация автора

Нет, нет способа достичь того, о чем ты говоришь.

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

В таких случаях вы можете использовать некоторые макросы / функции.

public bool Until(int val, int check)
{
   return !(val == check);
}

и использовать его как

do {
    //Things happen here
} while (Until(i, 15))
Автор: Tigran Размещён: 31.07.2012 09:27

1 плюс

125277 Репутация автора

Вы не можете создавать свои собственные синтаксические абстракции в C #, поэтому лучшее, что вы можете сделать, - это создать собственную функцию высшего порядка. Вы можете создать Actionметод расширения:

public static void DoUntil(this Action act, Func<bool> condition)
{
    do
    {
        act();
    } while (!condition());
}

Который вы можете использовать как:

int i = 1;
new Action(() => { Console.WriteLine(i); i++; }).DoUntil(() => i == 15);

хотя сомнительно, предпочтительнее ли это использовать do..whileнапрямую.

Автор: Lee Размещён: 31.07.2012 09:38

50 плюса

2721 Репутация автора

Решение

Microsoft предлагает Rolsyn API в качестве реализации компилятора C # с открытым API. Он содержит отдельные API для каждого из этапов конвейера компилятора: синтаксический анализ, создание символов, привязка, эмиссия MSIL. Вы можете предоставить собственную реализацию синтаксического синтаксического анализатора или расширить существующую для того, чтобы получить компилятор C # с любыми функциями, которые вам нужны.

Рослин ОСАГО

Давайте расширим язык C # с помощью Roslyn! В моем примере я заменяю оператор do-till с соответствующим do-while:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Roslyn.Compilers.CSharp;

namespace RoslynTest
{

    class Program
    {
        static void Main(string[] args)
        {

            var code = @"

            using System;

            class Program {
                public void My() {
                    var i = 5;
                    do {
                        Console.WriteLine(""hello world"");
                        i++;
                    }
                    until (i > 10);
                }
            }
            ";



            //Parsing input code into a SynaxTree object.
            var syntaxTree = SyntaxTree.ParseCompilationUnit(code);

            var syntaxRoot = syntaxTree.GetRoot();

            //Here we will keep all nodes to replace
            var replaceDictionary = new Dictionary<DoStatementSyntax, DoStatementSyntax>();

            //Looking for do-until statements in all descendant nodes
            foreach (var doStatement in syntaxRoot.DescendantNodes().OfType<DoStatementSyntax>())
            {
                //Until token is treated as an identifier by C# compiler. It doesn't know that in our case it is a keyword.
                var untilNode = doStatement.Condition.ChildNodes().OfType<IdentifierNameSyntax>().FirstOrDefault((_node =>
                {
                    return _node.Identifier.ValueText == "until";
                }));

                //Condition is treated as an argument list
                var conditionNode = doStatement.Condition.ChildNodes().OfType<ArgumentListSyntax>().FirstOrDefault();

                if (untilNode != null && conditionNode != null)
                {

                    //Let's replace identifier w/ correct while keyword and condition

                    var whileNode = Syntax.ParseToken("while");

                    var condition = Syntax.ParseExpression("(!" + conditionNode.GetFullText() + ")");

                    var newDoStatement = doStatement.WithWhileKeyword(whileNode).WithCondition(condition);

                    //Accumulating all replacements
                    replaceDictionary.Add(doStatement, newDoStatement);

                }

            }

            syntaxRoot = syntaxRoot.ReplaceNodes(replaceDictionary.Keys, (node1, node2) => replaceDictionary[node1]);

            //Output preprocessed code
            Console.WriteLine(syntaxRoot.GetFullText());

        }
    }
}
///////////
//OUTPUT://
///////////
//            using System;

//            class Program {
//                public void My() {
//                    var i = 5;
//                    do {
//                        Console.WriteLine("hello world");
//                        i++;
//                    }
//while(!(i > 10));
//                }
//            }

Теперь мы можем скомпилировать обновленное синтаксическое дерево с помощью API Roslyn или сохранить syntaxRoot.GetFullText () в текстовый файл и передать его в csc.exe.

Автор: Raman Zhylich Размещён: 31.07.2012 09:41

9 плюса

28760 Репутация автора

Большая недостающая часть цепляется за конвейер, иначе вы не намного дальше, чем это .Emitпредусмотрено. Не поймите меня неправильно, Roslyn приносит много замечательных вещей, но для тех из нас, кто хочет реализовать препроцессоры и метапрограммирование, кажется, что этого пока не было. Вы можете реализовать «предложения кода» или то, что они называют «проблемами» / «действиями», как расширение, но это в основном единичное преобразование кода, которое действует как предлагаемая внутренняя замена, а не как способ реализации нового языка. характерная черта. Это то, что вы всегда можете сделать с расширениями, но Roslyn значительно упрощает анализ / преобразование кода: введите описание изображения здесь

Из того, что я читал в комментариях разработчиков Roslyn на форумах codeplex, обеспечение хуков в конвейере не было первоначальной целью. Все новые функции языка C #, которые они предоставили в предварительном просмотре C # 6, включали изменение самого Roslyn. Так что вам по сути нужно раскошелиться на Рослин. У них есть документация о том, как собрать Roslyn и протестировать его с помощью Visual Studio. Это был бы сложный способ раскошелиться на Roslyn и использовать его в Visual Studio. Я говорю жестко, потому что теперь любой, кто хочет использовать ваши новые языковые функции, должен заменить компилятор по умолчанию вашим. Вы могли видеть, где это начало становиться грязным.

Сборка Roslyn и замена компилятора Visual Studio 2015 Preview собственной сборкой

Другой подход заключается в создании компилятора, который действует как прокси для Roslyn. Существуют стандартные API для построения компиляторов, которые VS может использовать. Это не тривиальная задача, хотя. Вы читали в файлах кода, вызывали API Roslyn для преобразования синтаксических деревьев и выдачи результатов.

Другая проблема с прокси-подходом состоит в том, чтобы заставить intellisense хорошо играть с любыми новыми языковыми функциями, которые вы реализуете. Вам, вероятно, придется иметь свой «новый» вариант C #, использовать другое расширение файла и реализовать все API, которые требуются Visual Studio для работы intellisense.

Наконец, рассмотрим экосистему C # и то, что будет означать расширяемый компилятор. Допустим, Roslyn действительно поддерживал эти ловушки, и это было так же просто, как предоставить пакет Nuget или расширение VS для поддержки новой языковой функции. Все ваши C #, использующие новую функцию Do-Пока, по сути недопустимы в C # и не будут компилироваться без использования вашего собственного расширения. Если вы идете достаточно далеко по этому пути с достаточным количеством людей, внедряющих новые функции, очень быстро вы обнаружите несовместимые языковые функции. Возможно, кто-то реализует синтаксис макроса препроцессора, но его нельзя использовать вместе с новым синтаксисом другого человека, потому что он использовал аналогичный синтаксис для определения начала макроса. Если вы используете множество проектов с открытым исходным кодом и копаетесь в их коде, вы столкнетесь с множеством странных синтаксисов, которые потребуют от вас отслеживания и изучения конкретных языковых расширений, которые использует проект. Этоможет быть безумием. Я не хочу звучать как скептик, так как у меня есть много идей о языковых особенностях, и я очень заинтересован в этом, но нужно рассмотреть последствия этого и то, насколько это можно поддерживать. Представьте себе, если вас наняли на работу где-то, и они внедрили все виды нового синтаксиса, который вам пришлось выучить, и без этих функций, проверенных так же, как функции C #, вы можете поспорить, что некоторые из них были бы не очень хорошо спроектированы / реализованы ,

Автор: AaronLS Размещён: 22.12.2014 07:37

5 плюса

59 Репутация автора

Вы можете проверить www.metaprogramming.ninja (я разработчик), он предоставляет простой способ выполнения языковых расширений (я даю примеры для конструкторов, свойств, даже функций в стиле js), а также полноценных DSL на основе грамматики.

Проект также с открытым исходным кодом. Вы можете найти документацию, примеры и т. Д. На github .

Надеюсь, это поможет.

Автор: Emilio Santos Размещён: 30.03.2015 05:18

1 плюс

222 Репутация автора

Я обнаружил, что самый простой способ расширить язык C # - это использовать текстовый процессор T4 для предварительной обработки моего источника. Скрипт T4 будет читать мой C # и затем вызывать синтаксический анализатор на основе Roslyn, который сгенерирует новый источник с сгенерированным пользователем кодом.

Во время сборки все мои сценарии T4 будут выполнены, таким образом, эффективно работая как расширенный препроцессор.

В вашем случае несовместимый код C # может быть введен следующим образом:

#if ExtendedCSharp
     do 
#endif
     {
                    Console.WriteLine("hello world");
                    i++;
     }
#if ExtendedCSharp
                until (i > 10);
#endif

Это позволило бы проверять синтаксис остальной части вашего (совместимого с C #) кода во время разработки вашей программы.

Автор: Wolfgang Grinfeld Размещён: 05.03.2019 08:28
Вопросы из категории :
32x32