macro TypedDataSetGenerator + патч компилятора
От: BOleg Россия  
Дата: 30.08.07 12:20
Оценка:
Привет всем.

Наваял макрос для генерации типизированного датасета из файла со схемой. Схему можно сгенерировать любым редактором датасета (например, в шарповском проекте) и подключить к немерловому проекту.
Использовать так:
namespace DataSets
{
  [assembly:DataSetFromXml(@"DataBase1.xml", @"DataBase1.n")]

  [assembly:DataSetFromSchema(@"DataBase2.xsd", @"DataBase2.n")]
}

def ds=DataSets.DataBase1();


Прежде чем привести код макроса, пара вопросов к знатокам компилятора/интеграции:
1. Позырьте генерации AST (метод Helper.IncludeFile), ибо то, что получилось — скорее хакерство. Там генерируется AST напрямую из немерлового кода. Может есть более другие пути?
2. В интеграции нужно бы выставлять перед парсингом очередного проекта его директорию как текущую. Иначе относительные пути при компиляции и работе IntelliSense оказываются разными (всплывающая подсказка над макросом: "cannot open file DataBase.n: Could not find file '(тут путь решения вместо проекта)\DataBase.n'").
3. Пришлось запатчить NemerleCodeGenerator, ибо нашел несколько ошибок (например, при генерации делегатов). Diff выложил здесь (залейте кто-нибудь, кто права имеет).
4. Может макрос тоже включить в стандартную поставку компилятора? Пригодится.

Код макроса:
  [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Assembly)]
  macro DataSetFromXml (sourceFile : string, nFile : string)
  {
    Helper.Generate(Nemerle.Macros.ImplicitCTX().Env, sourceFile, nFile,
      fun(x){
        def dataSet=DataSet();
        _=dataSet.ReadXml(x);
        dataSet;
      });
  }

  [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Assembly)]
  macro DataSetFromSchema (sourceFile : string, nFile : string)
  {
    Helper.Generate(Nemerle.Macros.ImplicitCTX().Env, sourceFile, nFile,
      fun(x){
        def dataSet=DataSet();
        dataSet.ReadXmlSchema(x);
        dataSet;
      });
  }

  static class Helper
  {
    public static Generate(env : GlobalEnv, sourceFile : string, nFile : string, loadFunc : string->DataSet) : void
    {
      //генерируем n-файл только при компиляции
      when(!ManagerClass.Instance.IsIntelliSenseMode && Helper.NeedReCreate(sourceFile, nFile))
      {
        mutable ds=
          try
          {
            loadFunc(sourceFile);
          }catch
          {
            | _e => 
              Message.FatalError("Error reading XML schema file $(_e.Message)");
          }

        Helper.GenerateCodeFile(ds, nFile);
      }

      Helper.IncludeFile(env, nFile);
    }

    static NeedReCreate(sourceFile : string, destinationFile : string) : bool
    {
      match(File.Exists(sourceFile), File.Exists(destinationFile))
      {
        | (false, _) => 
          Message.FatalError("Source file does not exists");
        | (true, true) => 
          File.GetLastWriteTime(sourceFile)>File.GetLastWriteTime(destinationFile);
        | _ => true;
      }
    }

    static GenerateCodeFile(ds : DataSet, nFile : string) : void
    {
      def ns=CodeNamespace();
      def codeGen : ICodeGenerator = NemerleCodeGenerator();

      TypedDataSetGenerator.Generate(ds, ns, codeGen);

      try
      {
        using(s=StreamWriter(nFile))
          codeGen.GenerateCodeFromNamespace(ns, s, CodeGeneratorOptions());
      }catch
      {
        | _e => 
          Message.FatalError("Error writing code file $(_e.Message)");
      }
    }

    static IncludeFile(env : GlobalEnv, nFile : string) : void
    {
      def compile(m : ClassMember.TypeDeclaration, tb : TypeBuilder = null)
      {
        match(m.td)
        {
          | _ is TopDeclaration.Delegate =>
            if(tb==null)
              _=env.Define(m);
            else
              tb.Define(m);

          | c is TopDeclaration.Class =>
            def ntb=if(tb==null)
              env.Define(m);
            else
              tb.DefineNestedType(m);
            ntb.Compile();

            _=c.decls.ForAll(member => {
              | td is ClassMember.TypeDeclaration => 
                compile(td, ntb);
                true;
              | _ => true;
            });
          | _ => {}
        }
      }

      def lexer=LexerFile(ManagerClass.Instance, nFile);
      try
      {
        def parsed=MainParser.Parse(lexer);
        _=parsed.ForAll(x => {
          def cd=ClassMember.TypeDeclaration(
            Splicable.Name(Name.NameInCurrentColor(x.Name, env)), 
            Modifiers(), 
            x);
          compile(cd);
          true;
        });
      }finally
      {
        Location.RemoveFile(nFile);
      }
    }
  }


Воть.
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.