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);
      }
    }
  }


Воть.
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
Re: macro TypedDataSetGenerator + патч компилятора
От: VladD2 Российская Империя www.nemerle.org
Дата: 30.08.07 13:58
Оценка: 1 (1)
Здравствуйте, BOleg, Вы писали:

BO>2. В интеграции нужно бы выставлять перед парсингом очередного проекта его директорию как текущую. Иначе относительные пути при компиляции и работе IntelliSense оказываются разными (всплывающая подсказка над макросом: "cannot open file DataBase.n: Could not find file '(тут путь решения вместо проекта)\DataBase.n'").


В Интеграции парсинг тел методов инкрементальный. Закладываться на текущий путь идея плохая. Лучше брать пути оностиельно пути файла в котором объявлен метатарибут. В макросе можно обратиться к локейшону и узнать в каком файле просходит обращение к макросу. Ну, а там уже рассчитывать относительные пути.

BO>3. Пришлось запатчить NemerleCodeGenerator, ибо нашел несколько ошибок (например, при генерации делегатов). Diff выложил здесь (залейте кто-нибудь, кто права имеет).


У меня не удаеся скачать данный файл. Видимо юрла кривая.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: macro TypedDataSetGenerator + патч компилятора
От: BOleg Россия  
Дата: 30.08.07 14:06
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>У меня не удаеся скачать данный файл. Видимо юрла кривая.

Хм. Вот:
Index: NemerleCodeGenerator.n
===================================================================
--- NemerleCodeGenerator.n (revision 7778)
+++ NemerleCodeGenerator.n (working copy)
@@ -132,7 +132,21 @@
if (initializers.Count > 0) {
output.WriteLine ("[");
++Indent;
— OutputExpressionList (initializers, true);
+
+ mutable first = true;
+ foreach (e is CodeExpression in initializers)
+ {
+ when(!first)
+ ContinueOnNewLine(", ");
+ (this:ICodeGenerator).GenerateCodeFromExpression(e, (output :> IndentedTextWriter).InnerWriter, options);
+ when(first)
+ {
+ output.Write(" : ");
+ output.Write(createType.BaseType);
+ }
+ first=false;
+ }
+
--Indent;
output.Write ("]");
}
@@ -155,8 +169,8 @@
output.Write(')');
}

— when (createType.BaseType == "System.Object" || createType.BaseType == "object")
— output.Write(" :> array[object]");
+// when (createType.BaseType == "System.Object" || createType.BaseType == "object")
+// output.Write(" :> array[object]");
}

protected override GenerateBaseReferenceExpression (_ : CodeBaseReferenceExpression) : void
@@ -504,7 +518,9 @@
++Indent;
foreach (clause is CodeCatchClause in statement.CatchClauses) {
output.Write ("| ");
— OutputTypeNamePair (clause.CatchExceptionType, GetSafeName (clause.LocalName));
+ Output.Write (GetSafeName (clause.LocalName));
+ Output.Write (" is ");
+ Output.Write (GetTypeOutput (clause.CatchExceptionType));
output.WriteLine (" =>");
++Indent;
GenerateStatements (clause.Statements);
@@ -717,7 +733,10 @@
();
}

— match (declaration) {
+ match (declaration)
+ {
+ | _ when (this.IsCurrentDelegate) =>
+ output.Write ("delegate ");
| d when d.IsStruct =>
when (d.IsPartial) {
output.Write ("partial ");
@@ -951,43 +970,44 @@

output.Write(' ');

— def enumerator = declaration.BaseTypes.GetEnumerator();
— when (enumerator.MoveNext()) {
— mutable ty = (enumerator.Current :> CodeTypeReference);

— output.Write(": ");
— OutputType(ty);
+ if(!this.IsCurrentDelegate)
+ {
+ def enumerator = declaration.BaseTypes.GetEnumerator();
+ when (enumerator.MoveNext()) {
+ mutable ty = (enumerator.Current :> CodeTypeReference);

— while (enumerator.MoveNext()) {
— ty = enumerator.Current :> CodeTypeReference;

— output.Write(", ");
+ output.Write(": ");
OutputType(ty);
+
+ while (enumerator.MoveNext()) {
+ ty = enumerator.Current :> CodeTypeReference;
+
+ output.Write(", ");
+ OutputType(ty);
+ }
+
+ output.Write(' ');
}

— output.Write(' ');
+ OutputBlockStart (BracingStyle);
+ ++Indent;
+ }else
+ {
+ def delegateDeclaration = declaration :> CodeTypeDelegate;
+ output.Write("(");
+ OutputParameters(delegateDeclaration.Parameters);
+ output.Write(") : ");
+ OutputType(delegateDeclaration.ReturnType);
+ output.WriteLine(";");
}
— if (declaration is CodeTypeDelegate)
— output.Write ("(");
— else
— OutputBlockStart (BracingStyle);
— ++Indent;
}

— protected override GenerateTypeEnd (declaration : CodeTypeDeclaration) : void
+ protected override GenerateTypeEnd (_declaration : CodeTypeDeclaration) : void
{
— --Indent;
— if (declaration is CodeTypeDelegate) {
— Output.Write (") : ");
— def ty = (declaration :> CodeTypeDelegate).ReturnType;
— if (ty != null)
— OutputType (ty);
— else
— Output.Write ("void");
— Output.WriteLine (";");
+ when(!this.IsCurrentDelegate)
+ {
+ --Indent;
+ OutputBlockEnd (BracingStyle);
}
— else
— OutputBlockEnd (BracingStyle);
}

protected override GenerateNamespace (ns : CodeNamespace) : void
@@ -1146,22 +1166,30 @@

when (attributes %&& MemberAttributes.New)
output.Write("new ");

+
match (attributes %& MemberAttributes.ScopeMask)
{
| MemberAttributes.Abstract =>
output.Write("abstract ");

— | MemberAttributes.Final =>
— output.Write("sealed ");

| MemberAttributes.Static => // TODO: is static mutually exclusive with other flags?
output.Write("static ");

| MemberAttributes.Override =>
output.Write("override ");
+
+ | MemberAttributes.Final =>
+ output.Write("");

— | _ => ()
+ | _ =>
+ match (attributes %& MemberAttributes.AccessMask)
+ {
+ | MemberAttributes.Assembly
+ | MemberAttributes.Family
+ | MemberAttributes.Public =>
+ output.Write("virtual ");
+ | _ => ()
+ }
}
}
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
Re[2]: macro TypedDataSetGenerator + патч компилятора
От: Блудов Павел Россия  
Дата: 31.08.07 03:18
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>У меня не удаеся скачать данный файл. Видимо юрла кривая.

Просто расширение .patch не прописано в конфиге.
... << RSDN@Home 1.2.0 alpha rev. 726>>
Re: macro TypedDataSetGenerator + патч компилятора
От: Sinclair Россия https://github.com/evilguest/
Дата: 31.08.07 04:41
Оценка: +1
Здравствуйте, BOleg, Вы писали:
BO>2. В интеграции нужно бы выставлять перед парсингом очередного проекта его директорию как текущую. Иначе относительные пути при компиляции и работе IntelliSense оказываются разными (всплывающая подсказка над макросом: "cannot open file DataBase.n: Could not find file '(тут путь решения вместо проекта)\DataBase.n'").
BO>3. Пришлось запатчить NemerleCodeGenerator, ибо нашел несколько ошибок (например, при генерации делегатов). Diff выложил здесь (залейте кто-нибудь, кто права имеет).
Обязательно пакуй всякую экзотику зипом перед выкладыванием на сервер RSDN.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: macro TypedDataSetGenerator + патч компилятора
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.08.07 13:21
Оценка:
Здравствуйте, BOleg, Вы писали:

Лучше зипани файл. А еще лучше заведи баг-репорт куда и приатач этот патч.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.