|
|
От: | Константин Л. | |
| Дата: | 20.10.08 18:03 | ||
| Оценка: | -1 | ||
macro @foreach (inexpr, body)
syntax ("foreach", "(", inexpr, ")", body)
{
match (ListComprehensionHelper.ExpandRange (inexpr, body)) {
| Some (expr) => Nemerle.Imperative.Return (expr)
| None => {}
}
def (iter, collection) =
match (inexpr) {
| <[ $i in $c ]> => (i, c)
| e =>
Message.FatalError ($ "the syntax is 'foreach (x in collection)', "
"got $e");
}
def typer = Macros.ImplicitCTX ();
def tcollection = typer.TypeExpr (collection);
// build the body of loop (may contain additional matching)
def build_definition (val) {
match (body) {
| <[ match ($(null)) { ..$cases } ]> =>
match (iter) {
| <[ $(x : name) ]> when char.IsLower (x.Id[0]) | <[ (..$_) ]> => ()
| _ => Message.FatalError ("only simple names available in pattern"
" of foreach with direct matching")
}
<[ def $iter = $val;
match ($iter) { ..$cases }
]>
| _ =>
def mat =
match (iter) {
| <[ $pat :> $ty ]> =>
<[ match ($val :> $ty) { | $pat => $body; () | _ => () } ]>
| _ =>
<[ match ($val) { | $iter => $body; () | _ => () } ]>
}
mat.cases.Iter (fun (x : PT.MatchCase) { x.disable_warnings = true });
mat
}
}
// here we choose if we want to use enumerator pattern
// of access GetEnumerator through IEnumarable
// http://www.jaggersoft.com/csharp_standard/15.8.4.htm
def decide_enumerator_pattern (tyinfo) {
def all = tyinfo.LookupMember ("GetEnumerator");
def choosen = List.Exists (all, fun (mem : IMember) {
| meth is IMethod when !meth.IsStatic && meth.GetParameters ().IsEmpty =>
match (meth.ReturnType.Fix ()) {
// FIXME: do additional conservative checks
| MType.Class (tc, _) when
!tc.LookupMember ("MoveNext").IsEmpty &&
!tc.LookupMember ("Current").IsEmpty => true
| _ => false
}
| _ => false
});
if (choosen)
<[ $(tcollection : typed).GetEnumerator () ]>
else
<[ ($(tcollection : typed) : System.Collections.IEnumerable).GetEnumerator () ]>
}
typer.DelayMacro (fun (fail_loudly) {
match (tcollection.Type.Hint) {
| Some (MType.Class (tc, args)) =>
if (tc.InternalType.Nemerle_list_tc != null
&& tc.SuperType (tc.InternalType.Nemerle_list_tc).IsSome)
{
def arg = List.Head (args);
def definition = build_definition (<[ x ]>);
Some (<[
// we explicitly set parameter type to list, because collection's type
// can be more specific (list.Cons, etc.)
($("_N_break" : global) : {
def foreach_loop (_ : list [$(arg : typed)]) {
| x :: xs =>
$("_N_continue" : global) : {
$definition;
}
foreach_loop (xs)
| _ => ()
}
foreach_loop ($(tcollection : typed))
})
]>)
}
else {
def init_body = decide_enumerator_pattern (tc);
def is_disposable =
typer.JustTry (fun () {
def expr = typer.TypeExpr (init_body);
expr.Type.Require (<[ ttype: System.IDisposable ]>)
});
def finally_body =
if (is_disposable)
<[ (enumerator : System.IDisposable).Dispose () ]>
else
<[
match (enumerator) {
| x is System.IDisposable => x.Dispose ();
| _ => ()
}
]>;
def definition = build_definition (<[ enumerator.Current ]>);
Some (<[
def enumerator = $init_body;
$("_N_break" : global) : {
def loop () {
when (enumerator.MoveNext ()) {
$("_N_continue" : global) : {
$definition;
}
loop ();
}
}
try { loop () }
finally { $finally_body }
}
]>)
}
| Some (MType.Array (_ , rank)) =>
def indices = array (rank);
def lengths = array (rank);
for (mutable i = 0; i < rank; ++i) {
indices [i] = Macros.NewSymbol ();
lengths [i] = Macros.NewSymbol ();
}
def indices_list = List.RevMap (List.FromArray (indices), fun (x) {
<[ $(x : name) ]>
});
def build_loops (depth) {
/// build expression defining iteration symbols
| 0 => build_definition (<[ cached_collection [..$indices_list] ]>)
| n =>
def idx = indices [n - 1];
<[ for (mutable $(idx : name) = 0;
$(idx : name) < $(lengths [n - 1] : name);
++ $(idx : name))
$(build_loops (n - 1))
]>
}
mutable sequence = [ <[ $(build_loops (rank)) ]> ];
if (rank == 1)
sequence = <[ def $(lengths [0] : name) = cached_collection.Length ]> :: sequence;
else
for (mutable i = rank - 1; i >= 0; --i)
sequence = <[ def $(lengths [(rank - 1) - i] : name) = cached_collection.GetLength ($(i : int)) ]>
:: sequence;
sequence = <[ def cached_collection = $(tcollection : typed) ]> :: sequence;
Some (<[ { .. $sequence } ]>)
| t =>
when (fail_loudly) {
def guess =
match (t) {
| Some (t) => $ "current best guess about the type is $t"
| None => "the compiler has no idea what the type might be"
}
Message.Error ($ "collection in foreach must be an array or "
"type implementing enumerator pattern, $guess");
Message.Hint ("try specifing the type directly using 'expr : SomeType'");
}
None ()
}
})
}КЛ>>Правда состоит в том, что при чтении, а не написании, кода, знать точное имя типа просто не нужно.
Потому что строгую типизацию придумали для того, чтобы компилятор мог автоматически проверять соответствие контрактов, и, как следствие, контроллировать до определенной степени корректность программы. Необходимость же явно аннотировать типы — вынужденное зло. И если компилятор умеет избавлять нас от этого зла, не снижая ни на йоту уровень контроля, от этого код становится только чище и меньше содержит не относящейся непосредственно к решаемой задаче информации.
Правда состоит в том[/b], что при чтении, а не написании, кода, знать точное имя типа просто не нужно.