Как сделать и найти метку в дереве AST
От: FDSC Россия consp11.github.io блог
Дата: 05.08.11 23:32
Оценка:
Пытаюсь в очередной раз изучить макросы, но не получается.

Нужно написать макросы, которые будут позволять:
первый макрос
1. Поставить на метод метку допустимого состояния вызова в некотором конечном автомате
2. Сгенерировать runtime-проверку в методе: что он вызван там, где надо

Второй макрос:
1. Поставить метку на часть кода, например, mark("имя метки") {делаем_нечто_страшное}
2. При компиляции метода найти выделенный этой меткой код и проанализировать его AST (например, чтобы вычислить правильную последовательность, заданную первым макросом не в runtime, а во время компиляции)

На второй макрос даже не представляю куда копать. Подскажите.

С первым макросом пытаюсь сделать что-то такое

using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;


namespace marks
{
    // Этим макросом пытаемся поставить метку на AST
  macro verificationMarkLogic (vlMarkName, logicTest, body)
  syntax ("vlMark", "(", vlMarkName, logicTest,  ")", body)
  {
      // Делаем кортеж из данных метки и тела метода
      def typle = vlMarkName::logicTest::body::[];
      <[
        def result = $(PExpr.Tuple.Create
        (
            typle
        ));

        // Возвращаем результат вычисления метода
        result[2]
        ]>
  }
  

    // Определяем макрос-атрибут метода
  [Nemerle.MacroUsage (Nemerle.MacroPhase.WithTypedMembers,
  Nemerle.MacroTargets.Method, Inherited = true)]
  // У макроса должно быть два параметра:
  //   строка-имя состояния конечного автомата, в которое переводится автомат после выполнения данного метода
  //   и список допустимых состояний, в которых можно вызывать данный метод
  macro verificationMarkState (tb : TypeBuilder, meth : MethodBuilder, vlStateName, vlAllowableStates)
  {
      // Если членов класса почему-то нет, то считаем это ошибкой
      if (!tb.LookupMemberAvailable)
      {
          def msg = "lookup member not available for class $(tb.Name) in the macro verificationMarkState";
          Message.Error(meth.Location, msg);
      }
      else
      {
          // Вводим служебную переменную
          def serviceMemberName = tb.Name + "_verificationMark_state";
          mutable flag = false;
          when (tb.LookupMember (serviceMemberName) == [])
            tb.Define (<[ decl: $serviceMemberName: string = "" ]>);

          // Генерируем новое тело метода
          def msg = "Not allowed state for class $(tb.Name) in method $(meth.Name)";
          meth.Body =
          <[
            // Проверяем, что текущее состояние позволяет вызывать данный метод
            def temporary_verification_flag = false;
            foreach (allowableState in vlAllowableStates)
            {
                when ( $serviceMemberName == allowableState)
                {
                    temporary_verification_flag = true;
                    break;
                }
            }
            assert(temporary_verification_flag, $msg);

            // Выполняем вычисления и меняем состояние, если не произошло исключение
            $(meth.Body);
            $serviceMemberName = $vlStateName;
          ]>
      }
  }


  public partial class MainForm : Form
  {
    public this()
    {
      InitializeComponent();
    }
  
    private button1_Click (_ : object,  _ : System.EventArgs) : void
    {
        markTest();
    }

    class File
    {
        [verificationMarkState("initialized", "":[])]
        public this()
        {
            _ = MessageBox.Show("File");
        }

        [verificationMarkState("opened", "initialized":[])]
        public open(): void
        {
            _ = MessageBox.Show("open");
        }

        public doing(): void
        {
            _ = MessageBox.Show("doing");
        }

        [verificationMarkState("closed", "opened":[])]
        public close(): void
        {
            _ = MessageBox.Show("Close");
        }
    }
    
    public markTest(): void
    {
        def f = File();
        f.open();
        f.doing();
        f.close();
        f.close();
    }
  }
}


Этот код при компиляции выдаёт

Build FAILED.

"D:\works\programming\AITryes\macroTryes\marks\marks.nproj" (Build target) (1)
->
(CoreCompile target) ->
MainForm.n(97,10,97,53): error : the custom attribute `verificationMarkState(
"initialized", ("" : []))' could not be found or is invalid [D:\works\programmi
ng\AITryes\macroTryes\marks\marks.nproj]

0 Warning(s)
1 Error(s)

Time Elapsed 00:00:00.83

верификация конечный_автомат
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.