Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
A little baby multi-dispatcher is born. It hasn't learned to do topol…
…ogical sorting yet, but it has the organs that will allow it to do so some day soon. :-)
  • Loading branch information
jnthn committed Aug 22, 2010
1 parent e1bf4d3 commit 2d77d23
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 4 deletions.
Expand Up @@ -28,10 +28,14 @@ public static List<RakudoCodeRef.Instance> FindCandidates(Context CallerScope, C
// Get the next outer scope, or alternatively start off with the
// caller scope.
CurScope = CurScope == null ? CallerScope : CurScope.Outer;
if (CurScope == null)
break;

// Any candidates here?


RakudoObject ScopeList;
if (CurScope.LexPad != null && CurScope.LexPad.TryGetValue(CandidateHolderName, out ScopeList))
foreach (var Candidate in (ScopeList as P6list.Instance).Storage)
Result.Add(Candidate as RakudoCodeRef.Instance);
} while (CurScope != ProtoScope);
return Result;
}
Expand Down
138 changes: 137 additions & 1 deletion dotnet/runtime/Runtime/MultiDispatch/MultiDispatcher.cs
Expand Up @@ -14,9 +14,145 @@ namespace Rakudo.Runtime.MultiDispatch
/// </summary>
public static class MultiDispatcher
{
/// <summary>
/// Finds the best candidate, if one exists, and returns it.
/// </summary>
/// <param name="Candidates"></param>
/// <param name="Capture"></param>
/// <returns></returns>
public static RakudoCodeRef.Instance FindBestCandidate(List<RakudoCodeRef.Instance> Candidates, RakudoObject Capture)
{
throw new NotImplementedException();
// Sort the candidates.
// XXX Cache this in the future.
var SortedCandidates = Sort(Candidates);

// Extract the native capture.
// XXX Handle non-native captures too.
var NativeCapture = Capture as P6capture.Instance;

// Now go through the sorted candidates and find the first one that
// matches.
var PossiblesList = new List<RakudoCodeRef.Instance>();
foreach (var Candidate in SortedCandidates)
{
// If we hit a null, we're at the end of a group.
if (Candidate == null)
{
if (PossiblesList.Count == 1)
return PossiblesList[0];
else if (PossiblesList.Count > 1)
// Here is where you'd handle constraints.
throw new Exception("Ambiguous dispatch: more than one candidate matches");
else
continue;
}

/* Check if it's admissable by arity. */
var NumArgs = NativeCapture.Positionals.Length;
if (NumArgs < Candidate.Sig.NumRequiredPositionals() ||
NumArgs > Candidate.Sig.NumPositionals())
continue;

/* Check if it's admissable by type. */
var TypeCheckCount = Math.Min(NumArgs, Candidate.Sig.NumPositionals());
var TypeMismatch = false;
for (int i = 0; i < TypeCheckCount; i++) {
var Arg = NativeCapture.Positionals[i];
var Type = Candidate.Sig.Parameters[i].Type;
if (Arg.STable.WHAT != Type && Type != null)
{
TypeMismatch = true;
break;
}
}
if (TypeMismatch)
continue;

/* If we get here, it's an admissable candidate; add to list. */
PossiblesList.Add(Candidate);
}

// If we get here, no candidates matched.
throw new Exception("No candidates found to dispatch to");
}

/// <summary>
/// Sorts the candidates.
/// </summary>
/// <param name="Unsorted"></param>
/// <returns></returns>
private static List<RakudoCodeRef.Instance> Sort(List<RakudoCodeRef.Instance> Unsorted)
{
var Sorted = new List<RakudoCodeRef.Instance>(Unsorted);
Sorted.Add(null);
return Sorted;
}

/// <summary>
/// Checks if one signature is narrower than another.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
private static int IsNarrower(RakudoCodeRef.Instance a, RakudoCodeRef.Instance b)
{
var Narrower = 0;
var Tied = 0;
int i, TypesToCheck;

/* Work out how many parameters to compare, factoring in slurpiness
* and optionals. */
if (a.Sig.NumPositionals() == b.Sig.NumPositionals())
TypesToCheck = a.Sig.NumPositionals();
else if (a.Sig.NumRequiredPositionals() == b.Sig.NumRequiredPositionals())
TypesToCheck = Math.Min(a.Sig.NumPositionals(), b.Sig.NumPositionals());
else
return 0;

/* Analyse each parameter in the two candidates. */
for (i = 0; i < TypesToCheck; i++) {
var TypeObjA = a.Sig.Parameters[i].Type;
var TypeObjB = b.Sig.Parameters[i].Type;
if (TypeObjA == TypeObjB)
{
/* In a full Perl 6 multi dispatcher, you'd consider
* constraints here. */
Tied++;
}
else
{
if (IsNarrowerType(TypeObjA, TypeObjB))
Narrower++;
else if (!IsNarrowerType(TypeObjB, TypeObjA))
Tied++;
}
}

/* If one is narrower than the other from current analysis, we're done. */
if (Narrower >= 1 && Narrower + Tied == TypesToCheck)
return 1;

/* If they aren't tied, we're also done. */
else if (Tied != TypesToCheck)
return 0;

/* Otherwise, we see if one has a slurpy and the other not. A lack of
* slurpiness makes the candidate narrower. Otherwise, they're tied. */
return !a.Sig.HasSlurpyPositional() && b.Sig.HasSlurpyPositional() ? 1 : 0;
}

/// <summary>
/// Compares two types to see if the first is narrower than the second.
/// XXX This is not complete yet, just very basic check.
/// </summary>
/// <returns></returns>
public static bool IsNarrowerType(RakudoObject A, RakudoObject B)
{
// For now, differentiate any (represented by null) and a type.
if (B != null && A == null)
return false;
else
return true;
}
}
}
2 changes: 1 addition & 1 deletion dotnet/runtime/Runtime/Ops.cs
Expand Up @@ -481,7 +481,7 @@ public static RakudoObject multi_dispatch_over_lexical_candidates(ThreadContext
var Candidate = MultiDispatch.MultiDispatcher.FindBestCandidate(
MultiDispatch.LexicalCandidateFinder.FindCandidates(
TC.CurrentContext.Caller,
TC.CurrentContext,
TC.CurrentContext.Outer,
"!" + Ops.unbox_str(TC, Name) + "-candidates"),
TC.CurrentContext.Capture);
return Candidate.STable.Invoke(TC, Candidate, TC.CurrentContext.Capture);
Expand Down
44 changes: 44 additions & 0 deletions dotnet/runtime/Runtime/Signatures/Signature.cs
Expand Up @@ -19,6 +19,50 @@ public Signature(Parameter[] Parameters)
this.Parameters = Parameters;
}

/// <summary>
/// Gets the number of positionals.
/// </summary>
/// <returns></returns>
internal int NumPositionals()
{
int Num = 0;
for (int i = 0; i < Parameters.Length; i++)
if (Parameters[i].Flags == Parameter.POS_FLAG)
Num++;
else if (Parameters[i].Flags == Parameter.OPTIONAL_FLAG)
Num++;
else
break;
return Num;
}

/// <summary>
/// Gets the number of required positionals.
/// </summary>
/// <returns></returns>
internal int NumRequiredPositionals()
{
int Num = 0;
for (int i = 0; i < Parameters.Length; i++)
if (Parameters[i].Flags == Parameter.POS_FLAG)
Num++;
else
break;
return Num;
}

/// <summary>
/// Do we have a slurpy positional parameter?
/// </summary>
/// <returns></returns>
internal bool HasSlurpyPositional()
{
for (int i = 0; i < Parameters.Length; i++)
if (Parameters[i].Flags == Parameter.POS_SLURPY_FLAG)
return true;
return false;
}

/// <summary>
/// The parameters we have.
/// </summary>
Expand Down

0 comments on commit 2d77d23

Please sign in to comment.