От:
Аноним
Дата: 04.08.13 10:55
Оценка:
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Console;
namespace Nemerle.Extensions1
{
/// Implements Equals and related methods, using the concept of
/// http://everything2.com/title/structural+equality
[MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Class, Inherited = false , AllowMultiple = false )]
public macro StructuralEquality(tb : TypeBuilder, params _options : list[PExpr])
{
StructuralEqualityImpl.RunBeforeInheritance(tb);
}
[MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Class, Inherited = false , AllowMultiple = false )]
public macro StructuralEquality(tb : TypeBuilder, params options : list[PExpr])
{
StructuralEqualityImpl.RunWithTypedMembers(tb, options);
}
[MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Field, Inherited = false , AllowMultiple = false )]
public macro EqualsIgnore(tb : TypeBuilder, field : ParsedField)
{
StructuralEqualityImpl.Ignore(tb, field );
}
[MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Property, Inherited = false , AllowMultiple = false )]
public macro EqualsIgnore(tb : TypeBuilder, prop : ParsedProperty)
{
StructuralEqualityImpl.Ignore(tb, prop);
}
module StructuralEqualityImpl
{
public IndexOf[T](this source : list[T], elem : T) : int
{
def loop(source, elem, index)
{
match (source)
{
| head :: _ when head.Equals(elem) => index
| _ :: tail => loop(tail, elem, index + 1)
| _ => -1
}
}
loop(source, elem, 0)
}
public NOfTypeRev[TIn, TFilter](this source : list[TIn]) : list[TFilter]
{
def loop(listIn : list[TIn], listOut : list[TFilter])
{
match (listIn)
{
| head :: tail =>
match (head)
{
| x is TFilter => loop(tail, x :: listOut)
| _ => loop(tail, listOut)
}
| _ => listOut
}
}
loop(source, [])
}
// used as keys in UserData
public IgnoredFieldsLabel : string = "StructuralEquality.IgnoredFields" ;
public IgnoredPropertiesLabel : string = "StructuralEquality.IgnoredProperties" ;
public IsEquatableImplementedLabel : string = "StructuralEquality.IsEquatableImplemented" ;
public IsStructuralEquatableImplementedLabel : string = "StructuralEquality.IsStructuralEquatableImplemented" ;
public StructuralEquatableTypeInfoLabel : string = "StructuralEquality.StructuralEquatableTypeInfoLabel" ;
// implements interfaces
public RunBeforeInheritance(tb : TypeBuilder) : void
{
def type = GetTypeName(tb);
if (tb.IsValueType || tb.IsSealed)
tb.Define(<[ decl:
private EqualsImpl($("other" : dyn) : $type ) : bool
{
} ]>);
else
tb.Define(<[ decl:
protected virtual EqualsImpl($("other" : dyn) : $type ) : bool
{
} ]>);
when (tb.Ast is TopDeclaration.Variant)
foreach (vo in tb.GetVariantOptions())
vo.Ast.AddCustomAttribute(<[ StructuralEquality ]>);
// Nemerle doesn't build if Tuples from stdlib are changed
unless (tb.IsVariantOption)
{
tb.AddImplementedInterface(<[ System.IEquatable.[$type ] ]>);
tb.UserData.Add(IsEquatableImplementedLabel, true );
// only .NET 4.0+ supports this
when (tb.Manager.NameTree.LookupExactType("System.Collections.IStructuralEquatable" ) is Some(iface))
{
tb.AddImplementedInterface(<[ System.Collections.IStructuralEquatable ]>);
tb.UserData.Add(IsStructuralEquatableImplementedLabel, true );
tb.UserData.Add(StructuralEquatableTypeInfoLabel, iface);
}
}
}
// parses options, defines methods
public RunWithTypedMembers(tb : TypeBuilder, options_expr : list[PExpr]) : void
{
//assert2(false);
def options = SEOptions.Parse(options_expr);
def get_relevant_fields(tb)
{
def all_fields = tb.GetFields(BindingFlags.Public %|
BindingFlags.NonPublic %|
BindingFlags.Instance %|
BindingFlags.DeclaredOnly);
// retrieve all ignored fields
def ignore_fields =
if (tb.UserData.Contains(IgnoredFieldsLabel))
tb.UserData[IgnoredFieldsLabel] :> List[string ]
else
List();
// ignored properties
when (tb.UserData.Contains(IgnoredPropertiesLabel))
{
def prop_list = tb.UserData[IgnoredPropertiesLabel] :> List[string ];
foreach (prop in prop_list)
{
match (tb.LookupMember(prop).Find(x => x is IProperty))
{
| Some(builder is PropertyBuilder) =>
match (builder.AutoPropertyField)
{
| Some(field ) => ignore_fields.Add(field .Name)
| _ => Message.Warning(builder.Location, $"$prop is not an autoproperty. No need to use EqualsIgnore" )
}
| _ => Message.Error($"Property $prop not found." )
}
}
}
ignore_fields.AddRange(options.IgnoreFields);
ignore_fields.Sort();
// remove ignored fields and return result
all_fields.Filter(x => ignore_fields.BinarySearch(x.Name) < 0);
}
// fields that are not ignored when evaluating structural equality
def relevant_fields = get_relevant_fields(tb);
// true if strict type equality is needed, i. e. no subtypes are allowed;
def typecheck_needed = !tb.IsSealed && !tb.IsVariantOption && !tb.IsValueType && options.CheckTypes;
//assert2(false);
DefineEquality(tb, relevant_fields, typecheck_needed, options.EmitDebugSources);
DefineHashCode(tb, relevant_fields, options.EmitDebugSources);
DefineOperators(tb);
DefineStructural(tb);
}
// adds a field to ignore list
public Ignore(tb : TypeBuilder, field : ClassMember.Field) : void
{
unless (tb.UserData.Contains(IgnoredFieldsLabel))
tb.UserData.Add(IgnoredFieldsLabel, List.[string ]());
def lst = tb.UserData[IgnoredFieldsLabel] :> List[string ];
unless (lst.Contains(field .Name))
lst.Add(field .Name);
}
// adds a property to ignore list
public Ignore(tb : TypeBuilder, prop : ClassMember.Property) : void
{
unless (tb.UserData.Contains(IgnoredPropertiesLabel)) tb.UserData.Add(IgnoredPropertiesLabel, List.[string ]());
def lst = tb.UserData[IgnoredPropertiesLabel] :> List[string ];
unless (lst.Contains(prop.Name)) lst.Add(prop.Name);
}
// represents macro options
[Record]
struct SEOptions
{
public IgnoreFields : list[string ] { get }
public CheckTypes : bool { get }
public EmitDebugSources : bool { get }
public static Default : SEOptions { get ; default SEOptions([], true , true ) }
public static Parse(options : list[PExpr]) : SEOptions
{
mutable check_types = true ;
mutable ignore_fields = [];
mutable emitDebugSources = true ;
foreach (opt in options)
{
| <[ CheckTypes = true ]> => check_types = true ;
| <[ CheckTypes = false ]> => check_types = false ;
| <[ Ignore = [..$flds] ]>
| <[ Ignore = $fld ]> with flds = [fld] =>
// add field names as strings
ignore_fields += flds.MapFiltered(_ is PExpr.Ref, x => (x :> PExpr.Ref).name.Id)
| <[ EmitDebugSources = true ]> => emitDebugSources = true ;
| <[ EmitDebugSources = false ]> => emitDebugSources = false ;
| _ => Message.Error("Unknown options for StructuralEquality." )
}
SEOptions(ignore_fields, check_types, emitDebugSources)
}
}
GetEqualsImpl(typeInfoToLookup : TypeInfo, paramType : FixedType.Class) : option[IMethod]
{
def baseEqualsImpl = typeInfoToLookup
.LookupMember("EqualsImpl" )
.NOfTypeRev.[_ , IMethod]()
.Find(m => m.Header.Parameters.Length == 1 && m.Header.Parameters.Head.Type.Equals(paramType));
baseEqualsImpl
}
IsParentImplement_EqualsImpl(tb : TypeBuilder) : bool
{
GetEqualsImpl(tb.BaseType, tb.BaseClass).IsSome
}
HasTypedEquals(type : FixedType) : bool
{
| Class(ti, _ ) =>
def result = ti
.LookupMember("Equals" )
.NOfTypeRev.[_ , IMethod]()
.Find(m => m.Header.Parameters.Length == 1 && m.Header.Parameters.Head.Type.Equals(type ));
result.IsSome
| _ => false
}
DefineEquality(tb : TypeBuilder, fields : Seq[IField], _check_types : bool , emitDebugSources : bool ) : void
{
assert2(tb.BaseType.LookupMemberAvailable);
def defineMember(member : ClassMember) : void
{
if (emitDebugSources) _ = tb.DefineWithSource(member)
else tb.Define(member);
}
// generates comparison code for a single field
def invokeEquals(x : IField)
{
def type = x.GetMemType();
def value = <[ $(x.Name : usesite) ]>;
if (type .IsPrimitive)
<[ this .$value == other.$value ]> // primitive magic
else if (type .Equals(tb.InternalType.String))
<[ string .Equals(this .$value , other.$value ) ]>
else if (type .IsValueType && !type .CanBeNull && HasTypedEquals(type ))
<[ this .$value .Equals(other.$value ) ]> // no null-checks
// <[ if ($value.HasValue) other.$value.HasValue && this.$value.Value.Equals(other.$value.Value); else !other.$value.HasValue; ]> // For T?
//else if (type is FixedType.StaticTypeVarRef) // for type parameters
// <[ EqualityComparer.Default.Equals($value, other.$value) ]>
else
<[ EqualityComparer.Default.Equals(this .$value , other.$value ) ]>;
}
def type = GetTypeName(tb);
//def type_checker =
// if (check_types)
// <[ other.GetType().Equals(this.GetType()) ]>
// else
// <[ true ]>;
def isParentImplement_EqualsImpl = IsParentImplement_EqualsImpl(tb);
def parentType = tb.BaseClass;
def baseCall =
if (isParentImplement_EqualsImpl)
<[ base .EqualsImpl(other : $(parentType : typed)) ]>
else
<[ true ]>;
// core comparison code (type checker + comparison for each field)
def body = fields.Fold(baseCall, (f, acc) => <[ $(invokeEquals(f)) && $acc ]> );
// no null-check for structs
def fun_body = if (tb.GetMemType().CanBeNull) <[ match (other) { | null => false | _ => $body } ]> else body;
def fun_body = <[
_ = other; // shut the compiler up if body degrades to "true"
$fun_body
]>;
match (GetEqualsImpl(tb, tb.GetMemType()))
{
| Some(method is MethodBuilder) =>
method .Body = fun_body;
//assert2(false);
method .Ast.Body = fun_body;
when (emitDebugSources)
tb.TyManager.GenerateFakeSourceCode(tb, method .Ast);
| _ => Message.FatalError($"Can't find EqualsImpl(other : $(tb.GetMemType())) in $tb" );
}
def implementsEquatable = AskUserData(tb, IsEquatableImplementedLabel);
if (implementsEquatable)
defineMember(<[ decl:
public Equals(other : $type ) : bool implements System.IEquatable.[$type ].Equals
{
EqualsImpl(other)
} ]>);
else
defineMember(<[ decl:
public Equals(other : $type ) : bool
{
EqualsImpl(other)
} ]>);
when (isParentImplement_EqualsImpl)
defineMember(<[ decl:
protected override EqualsImpl(other : $(parentType : typed)) : bool
{
| x is $type => EqualsImpl(x)
| _ => false
}
]>);
// implements object.Equals
defineMember(<[ decl:
public override Equals(other : System.Object) : bool
{
| x is $type => EqualsImpl(x)
| _ => false
}
]>);
}
DefineHashCode(tb : TypeBuilder, fields : Seq[IField], emitDebugSources : bool ) : void
{
def callBase = IsParentImplement_EqualsImpl(tb);
def (countConst, hash_bodyConst, hash_bodyMutable) = fields.Fold((0,<[0x011C9DC5^0x80000000]>,<[_hashCode]>),
fun ( f, (countConst, initExprConst, initExprMutable))
{
def type = f.GetMemType();
def value = <[ $(f.Name : usesite) ]>;
if (f.Name!="_hashCode" )
{
def gethashcode =
if (type .Equals(tb.InternalType.Int32))
<[ $value ]>
else if (type .Equals(tb.InternalType.UInt32))
<[ $value :> int ]>
else if (type .Equals(tb.InternalType.Boolean))
<[ if ($value ) 0x959595C3C3 else 0xC953C953 ]>
else
if (type .IsValueType)
if (type .CanBeNull) <[if ($value is null ) 0x359CC953 else $value .GetHashCode()]> else <[$value .GetHashCode()]>
else
if (type is FixedType.StaticTypeVarRef) <[ EqualityComparer.Default.GetHashCode($value ) ]> else <[if ($value is null ) 0x359CC953 else $value .GetHashCode()]>;
//else if (type.IsValueType)
// if (type.CanBeNull)
// <[ EqualityComparer.Default.GetHashCode($value) ]> // можно оптимизировать
// else
// <[ this.$value.GetHashCode() ]>
//else if (type is FixedType.StaticTypeVarRef)
// <[ EqualityComparer.Default.GetHashCode($value) ]>
//else
// <[ this.$value?.GetHashCode() ]>;
if (!f.IsMutable&(type .IsPrimitive||type .Equals(tb.InternalType.String)))
(
countConst+1,
<[ (_ *_ ) (0x01000193, (($initExprConst) ^ ($gethashcode)) )]>,
<[$initExprMutable]>
)else
(
countConst,
<[ $initExprConst]>,
<[ (_ *_ ) (0x01000193, (($initExprMutable) ^ ($gethashcode)) )]>
);
} else
(
countConst,
<[ $initExprConst]>,
<[ $initExprMutable]>
);
});
def variantOptionHash =
if (tb.IsVariantOption)
{
def optionNumber = tb.GetVariantOptionParent().GetVariantOptions().IndexOf(tb) + 1;
assert(optionNumber > 0);
<[ $(optionNumber : int ); ]>
}
else
<[ 0 ]>;
def baseHash =
if (callBase)
<[ base .GetHashCode(); ]>
else
<[ 0 ]>;
def body_hashcode =
<[
unchecked
{
when (_hashCode==0) _hashCode= $hash_bodyConst;
(_ +_ )(((_ ^_ ) ($hash_bodyMutable,$baseHash)),$variantOptionHash) : int ;
}
]>;
def body =
<[
unchecked
{
def _hashCode= $hash_bodyConst;
(_ +_ )(((_ ^_ ) ($hash_bodyMutable,$baseHash)),$variantOptionHash) : int ;
}
]>;
def define(expr)
{
if (emitDebugSources) _ = tb.DefineWithSource(expr)
else tb.Define(expr)
}
if (fields.Exists((t)=>{t.Name=="_hashCode" })||countConst>=7)
{
define(<[ decl: public mutable _hashCode : int ; ]> );
define(<[ decl: public override GetHashCode() : int { $body_hashcode } ]>);
}else
{
define(<[ decl: public override GetHashCode() : int { $body } ]>);
}
}
DefineOperators(tb : TypeBuilder) : void
{
def type = GetTypeName(tb);
if (tb.IsValueType)
{
tb.Define(<[ decl:
public static @==(first : $type , second : $type ) : bool
{
first.Equals(second)
}
]>);
}
else
{
tb.Define(<[ decl:
public static @==(first : $type , second : $type ) : bool
{
if (first is null ) second is null else first.Equals(second)
}
]>);
}
tb.Define(<[ decl:
public static @!= (first : $type , second : $type ) : bool
{
!(first == second)
}
]>);
}
DefineStructural(tb : TypeBuilder) : void
{
when (AskUserData(tb, IsStructuralEquatableImplementedLabel))
unless (tb.BaseType?.IsDerivedFrom(tb.UserData[StructuralEquatableTypeInfoLabel] :> TypeInfo))
{
tb.Define(<[ decl:
public Equals(other : object , _comparer : System.Collections.IEqualityComparer) : bool
{
Equals(other);
}
]>);
tb.Define(<[ decl:
public GetHashCode(_comparer : System.Collections.IEqualityComparer) : int
{
GetHashCode();
}
]>);
}
}
//MarkCompilerGenerated(cm : ClassMember) : ClassMember
//{
// cm.AddCustomAttribute(<[System.Runtime.CompilerServices.CompilerGenerated]>);
// cm
//}
// no api to get type name with params; 'this' keyword in this context is bugged
GetTypeName(tb : TypeBuilder) : PExpr
{
//<[ $(tb.GetMemType() : typed) ]>
def splicable_to_ref(s : Splicable)
{
| Name(n)
| HalfId(n) => PExpr.Ref(n)
| Expression(e) => e
}
def qname = PExpr.FromQualifiedIdentifier(tb.Manager, tb.Ast.FullQualifiedName);
if (tb.Ast.TypeParameters is null )
<[ $qname ]>
else
{
def args = tb.Ast.TypeParameters.tyvars.Map(splicable_to_ref);
<[ $qname.[..$args] ]>
}
}
AskUserData(tb : TypeBuilder, question : string , defaultAnswer : bool = false ) : bool
{
if (!tb.UserData.Contains(question)) defaultAnswer else tb.UserData[question] :> bool
}
}
}
От:
Аноним
Дата: 06.08.13 20:29
Оценка:
Может есть смысл попробывать данный вариант.
Re[2]: Nemerle.Extensions1
От:
catbert
Дата: 07.08.13 15:43
Оценка:
Здравствуйте, Аноним, Вы писали:
А>Может есть смысл попробывать данный вариант.
Попробуйте оформить это как
pull-request в
гитхабе .
Но сразу в неймспейс Nemerle.Extensions.
Разработчикам языка намного проще принять пулл-реквест, поэтому намного выше вероятность каких-то действий.
Здравствуйте, Аноним, Вы писали:
Было бы очень хорошим ходом разъяснить что этот код делает и какую проблему решает.
Ну и в самом коде комментарии тоже были бы полезны.
P.S.
IndexOf и NOfTypeRev: А разве нет уже похожих функций в стандартной библиотеке языка ?
Здравствуйте, Аноним, Вы писали:
Кстати, я бы хотел от макроса StructuralEquality следующее:
1. Поддержка variant
2. Возможность вынесения в отдельные классы без затрагивания оригинальных.
Re[2]: Nemerle.Extensions1
От:
catbert
Дата: 08.08.13 10:12
Оценка:
Здравствуйте, _NN_, Вы писали:
_NN>2. Возможность вынесения в отдельные классы без затрагивания оригинальных.
Это как?
Re[3]: Nemerle.Extensions1
Здравствуйте, catbert, Вы писали:
C>Здравствуйте, _NN_, Вы писали:
_NN>>2. Возможность вынесения в отдельные классы без затрагивания оригинальных.
C>Это как?
Ну скажем пример:
variant A { ... }
/*
Создаст
namespace AA
{
class AEqualityComparer : System.Collections.Generic.EqualityComparer[A]
{
GetHashCode()...
Equals(...)..
}
}
*/
[assembly : StructuralEquality(A, Namespace = "AA" )]
Re[2]: Nemerle.Extensions1
От:
Аноним
Дата: 08.08.13 12:43
Оценка:
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Аноним, Вы писали:
_NN>Кстати, я бы хотел от макроса StructuralEquality следующее:
_NN>1. Поддержка variant
_NN>2. Возможность вынесения в отдельные классы без затрагивания оригинальных.
1) Поддержка вариант там есть.
2) Что бы понять не хватает данных.
Re[3]: Nemerle.Extensions1
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, _NN_, Вы писали:
_NN>>Здравствуйте, Аноним, Вы писали:
_NN>>Кстати, я бы хотел от макроса StructuralEquality следующее:
_NN>>1. Поддержка variant
_NN>>2. Возможность вынесения в отдельные классы без затрагивания оригинальных.
А>1) Поддержка вариант там есть.
А есть также поддержка Include, Exclude как в оригинальном макросе ?
А>2) Что бы понять не хватает данных.
Ну я в соседнем сообщении описал.
Re[4]: Nemerle.Extensions1
От:
Аноним
Дата: 08.08.13 13:07
Оценка:
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Аноним, Вы писали:
А>>Здравствуйте, _NN_, Вы писали:
_NN>>>Здравствуйте, Аноним, Вы писали:
_NN>>>Кстати, я бы хотел от макроса StructuralEquality следующее:
_NN>>>1. Поддержка variant
_NN>>>2. Возможность вынесения в отдельные классы без затрагивания оригинальных.
А>>1) Поддержка вариант там есть.
_NN>А есть также поддержка Include, Exclude как в оригинальном макросе ?
Это и есть оригинальный, только изменил генерацию хешкода на существенно более лучшую (использовал умножение) и немного оптимизировал генерируемый код. Кроме того добавил приват поле хешкода генерируемое если неизменяемых значений 7 и более.
А>>2) Что бы понять не хватает данных.
_NN>Ну я в соседнем сообщении описал.
Так я не понял. Размещение процедурн в другом неймспейсе? какой профит. Мне что бы понять не хватает знаний.
Re[5]: Nemerle.Extensions1
Здравствуйте, Аноним, Вы писали:
А>>>1) Поддержка вариант там есть.
_NN>>А есть также поддержка Include, Exclude как в оригинальном макросе ?
А>Это и есть оригинальный, только изменил генерацию хешкода на существенно более лучшую (использовал умножение) и немного оптимизировал генерируемый код. Кроме того добавил приват поле хешкода генерируемое если неизменяемых значений 7 и более.
Ясно.
Тогда
баг остался
А>>>2) Что бы понять не хватает данных.
_NN>>Ну я в соседнем сообщении описал.
А>Так я не понял. Размещение процедурн в другом неймспейсе? какой профит. Мне что бы понять не хватает знаний.
Дело в том что в некоторых случаях не хочется трогать оригинальный класс.
Более того возможны разные варианты сравнения, как это реализовать ?
Пример:
variant A
{
| X { Name : string ; Field : int ; }
}
[assembly : StructuralEquality(A, Ignore = Name, Namespace = WithoutName)]
[assembly : StructuralEquality(A, Ignore = Field, Namespace = WithoutField)]
[assembly : StructuralEquality(A, Namespace = "Default" )]
И тогда можно сравнивать именно так как хотим:
def a = A.X("a" , 10);
def b = A.X("b" , 10);
WithoutName.AEqualityComparer().Equals(a, b) == true
WithoutField.AEqualityComparer().Equals(a, b) == false
Default.AEqualityComparer().Equals(a, b) == false
Re[6]: Nemerle.Extensions1
От:
Аноним
Дата: 08.08.13 16:06
Оценка:
http://files.mail.ru/B6BD1A6E524F40378F91A91537E343B0
Поправил баг.
[assembly: StructuralEquality(A, Namespace = "Default")]
не пойму как разбирать.
Re[7]: Nemerle.Extensions1
Здравствуйте, Аноним, Вы писали:
А>http://files.mail.ru/B6BD1A6E524F40378F91A91537E343B0
А>Поправил баг.
А>[assembly: StructuralEquality(A, Namespace = "Default")]
А>не пойму как разбирать.
Также как и обычный макрос разбирает:
[MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Class, Inherited = false , AllowMultiple = false )]
public macro StructuralEquality(tb : TypeBuilder, params options : list[PExpr] )
{
StructuralEqualityImpl.RunWithTypedMembers(tb, options);
}
...
[Record]
struct SEOptions
{
public IgnoreFields : list[string ] { get }
public CheckTypes : bool { get }
public EmitDebugSources : bool { get }
public static Default : SEOptions { get ; default SEOptions([], true , true ) }
public static Parse(options : list[PExpr]) : SEOptions
{
mutable check_types = true ;
mutable ignore_fields = [];
mutable emitDebugSources = true ;
foreach (opt in options)
{
| <[ CheckTypes = true ]> => check_types = true ;
| <[ CheckTypes = false ]> => check_types = false ;
| <[ Ignore = [..$flds] ]>
| <[ Ignore = $fld ]> with flds = [fld] =>
// add field names as strings
ignore_fields += flds.MapFiltered(_ is PExpr.Ref, x => (x :> PExpr.Ref).name.Id)
| <[ EmitDebugSources = true ]> => emitDebugSources = true ;
| <[ EmitDebugSources = false ]> => emitDebugSources = false ;
| _ => Message.Error("Unknown options for StructuralEquality." )
}
SEOptions(ignore_fields, check_types, emitDebugSources)
}
}
Выражение приходит в формате PExpr который сопоставляется с образцом как любой другой параметр.
Пример макроса уровня сборки:
GenerateTypedJS.n .
Кстати, изменения лучше было бы всего оформлять в виде пул реквеста.
Сначала делаете форк на
http://github.com/rsdn/nemerle
Потом добавляете изменения в ваш форк.
И там появляется кнопка 'Pull Request'.
Так можно следить и за историей изменений и легко их добавить в основной репозиторий.
Re[8]: Nemerle.Extensions1
От:
Аноним
Дата: 09.08.13 06:48
Оценка:
Здравствуйте, _NN_, Вы писали:
Не ясно как быть с хешкодом тогда.
Re[8]: Nemerle.Extensions1
От:
Аноним
Дата: 09.08.13 10:00
Оценка:
Здравствуйте, _NN_, Вы писали:
[StructuralEquality(Ignore = name, Ignore = other, Namespace="777" )]
[StructuralEquality(Ignore = name, Ignore = other, Namespace="999" )]
variant X
{
| A { name : string ;name2 : string ; other : int ; }
| B
}
Такой код что ли?
он не работает
Re[9]: Nemerle.Extensions1
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, _NN_, Вы писали:
А>Не ясно как быть с хешкодом тогда.
Хм.. согласен, тут надо это продумать хорошо.
Можно например в макросе указывать для типов чтобы пользоавться таким-то EqualityComparer-ом.
К примеру
[assembly : StructuralEquality(MyVariant, Namespace = M,
EqualityComparers = [(string , MyEqualityComparer), (int , MyIntComparer)])
А то что не перечислено будет пользоваться обычным obj.GetHashCode().
P.S.
Это предложение, код не рабочий.
Re[7]: Nemerle.Extensions1
Здравствуйте, Аноним, Вы писали:
А>http://files.mail.ru/B6BD1A6E524F40378F91A91537E343B0
А>Поправил баг.
Есть пару мест в вашем коде закомментированных.
Не ясно это ошибка или преднамеренно.
Если преднамеренно, что лучше такие места вычистить , чтобы не было вопросов.
Re[8]: Nemerle.Extensions1
От:
Аноним
Дата: 09.08.13 18:45
Оценка:
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Аноним, Вы писали:
А>>http://files.mail.ru/B6BD1A6E524F40378F91A91537E343B0
А>>Поправил баг.
_NN>Есть пару мест в вашем коде закомментированных.
_NN>Не ясно это ошибка или преднамеренно.
_NN>Если преднамеренно, что лучше такие места вычистить , чтобы не было вопросов.
приучили старый код не удалять, а комментировать, чо бы было понятно откуда что растет.
Re[9]: Nemerle.Extensions1
Здравствуйте, Аноним, Вы писали:
_NN>>Если преднамеренно, что лучше такие места вычистить , чтобы не было вопросов.
А>приучили старый код не удалять, а комментировать, чо бы было понятно откуда что растет.
Для этого есть система контроля версий:
https://github.com/rsdn/nemerle/tree/StructuralEquality
Кстати, тут подумалось что возможно полезной функцией было бы возможное указание более полного типа в Ignore:
[StructuralEquality(Ignore = A .name)]
variant A
{
| X { name : string ; other : int ; }
| Y { name : string ; }
}
A.X("a" , 1) == A.X("b" , 1)
A.Y("a" ) != A.Y("b" )
Re[2]: Nemerle.Extensions1
Здравствуйте, _NN_, Вы писали:
_NN>Кстати, я бы хотел от макроса StructuralEquality следующее:
_NN>1. Поддержка variant
Она вроде как есть. Или ее сломали?
_NN>2. Возможность вынесения в отдельные классы без затрагивания оригинальных.
А смысл? Все равно они не могут применяться для внешних типов.
Есть логика намерений и логика обстоятельств,
последняя всегда сильнее .
Пока на собственное сообщение не было ответов, его можно удалить.
Удалить