forked from jasder/antlr
Updated RuleDependencyChecker to match the new behavior of RuleDependencyProcessor
This commit is contained in:
parent
5e49211455
commit
6b7eb5dad4
|
@ -1 +1 @@
|
|||
Subproject commit dde8d7b3178c9654db7eccbbf6c629b4ed2e7fd4
|
||||
Subproject commit 8dedc2f0957b5f242059d750b3148364c49ffc4a
|
|
@ -33,6 +33,8 @@ using System.Reflection;
|
|||
using System.Security;
|
||||
using System.Text;
|
||||
using Antlr4.Runtime;
|
||||
using Antlr4.Runtime.Atn;
|
||||
using Antlr4.Runtime.Misc;
|
||||
using Sharpen;
|
||||
using Sharpen.Annotation;
|
||||
using Sharpen.Logging;
|
||||
|
@ -54,9 +56,7 @@ namespace Antlr4.Runtime.Misc
|
|||
{
|
||||
return;
|
||||
}
|
||||
IList<Type> typesToCheck = new List<Type>();
|
||||
typesToCheck.AddItem(dependentClass);
|
||||
Sharpen.Collections.AddAll(typesToCheck, dependentClass.GetDeclaredClasses());
|
||||
IList<Type> typesToCheck = GetTypesToCheck(dependentClass);
|
||||
foreach (Type clazz in typesToCheck)
|
||||
{
|
||||
if (IsChecked(clazz))
|
||||
|
@ -69,10 +69,49 @@ namespace Antlr4.Runtime.Misc
|
|||
{
|
||||
continue;
|
||||
}
|
||||
IDictionary<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>> recognizerDependencies
|
||||
= new Dictionary<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>>();
|
||||
foreach (Tuple<RuleDependency, IAnnotatedElement> dependency in dependencies)
|
||||
{
|
||||
Type recognizerType = dependency.Item1.Recognizer();
|
||||
IList<Tuple<RuleDependency, IAnnotatedElement>> list = recognizerDependencies.Get
|
||||
(recognizerType);
|
||||
if (list == null)
|
||||
{
|
||||
list = new List<Tuple<RuleDependency, IAnnotatedElement>>();
|
||||
recognizerDependencies.Put(recognizerType, list);
|
||||
}
|
||||
list.AddItem(dependency);
|
||||
}
|
||||
foreach (KeyValuePair<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>> entry
|
||||
in recognizerDependencies.EntrySet())
|
||||
{
|
||||
//processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format("ANTLR 4: Validating %d dependencies on rules in %s.", entry.getValue().size(), entry.getKey().toString()));
|
||||
CheckDependencies(entry.Value, entry.Key);
|
||||
}
|
||||
CheckDependencies(dependencies, dependencies[0].Item1.Recognizer());
|
||||
}
|
||||
}
|
||||
|
||||
private static IList<Type> GetTypesToCheck<_T0>(Type<_T0> clazz)
|
||||
{
|
||||
ISet<Type> result = new HashSet<Type>();
|
||||
GetTypesToCheck(clazz, result);
|
||||
return new List<Type>(result);
|
||||
}
|
||||
|
||||
private static void GetTypesToCheck<_T0>(Type<_T0> clazz, ISet<Type> result)
|
||||
{
|
||||
if (!result.AddItem(clazz))
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (Type declared in clazz.GetDeclaredClasses())
|
||||
{
|
||||
GetTypesToCheck(declared, result);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsChecked<_T0>(Type<_T0> clazz)
|
||||
{
|
||||
lock (checkedTypes)
|
||||
|
@ -90,40 +129,160 @@ namespace Antlr4.Runtime.Misc
|
|||
}
|
||||
|
||||
private static void CheckDependencies<_T0>(IList<Tuple<RuleDependency, IAnnotatedElement
|
||||
>> dependencies, Type<_T0> recognizerClass) where _T0:Recognizer<object, object
|
||||
>> dependencies, Type<_T0> recognizerType) where _T0:Recognizer<object, object
|
||||
>
|
||||
{
|
||||
string[] ruleNames = GetRuleNames(recognizerClass);
|
||||
int[] ruleVersions = GetRuleVersions(recognizerClass, ruleNames);
|
||||
StringBuilder incompatible = new StringBuilder();
|
||||
string[] ruleNames = GetRuleNames(recognizerType);
|
||||
int[] ruleVersions = GetRuleVersions(recognizerType, ruleNames);
|
||||
RuleDependencyChecker.RuleRelations relations = ExtractRuleRelations(recognizerType
|
||||
);
|
||||
StringBuilder errors = new StringBuilder();
|
||||
foreach (Tuple<RuleDependency, IAnnotatedElement> dependency in dependencies)
|
||||
{
|
||||
if (!recognizerClass.IsAssignableFrom(dependency.Item1.Recognizer()))
|
||||
if (!dependency.Item1.Recognizer().IsAssignableFrom(recognizerType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (dependency.Item1.Rule() < 0 || dependency.Item1.Rule() >= ruleVersions.Length)
|
||||
// this is the rule in the dependency set with the highest version number
|
||||
int effectiveRule = dependency.Item1.Rule();
|
||||
if (effectiveRule < 0 || effectiveRule >= ruleVersions.Length)
|
||||
{
|
||||
incompatible.Append(string.Format("Element %s dependent on unknown rule %d@%d in %s\n"
|
||||
, dependency.Item2.ToString(), dependency.Item1.Rule(), dependency.Item1.Version
|
||||
(), dependency.Item1.Recognizer().Name));
|
||||
string message = string.Format("Rule dependency on unknown rule %d@%d in %s%n", dependency
|
||||
.Item1.Rule(), dependency.Item1.Version(), dependency.Item1.Recognizer().ToString
|
||||
());
|
||||
errors.Append(message);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
EnumSet<Dependents> dependents = EnumSet.Of(Dependents.Self, dependency.Item1.Dependents
|
||||
());
|
||||
ReportUnimplementedDependents(errors, dependency, dependents);
|
||||
BitSet checked = new BitSet();
|
||||
int highestRequiredDependency = CheckDependencyVersion(errors, dependency, ruleNames
|
||||
, ruleVersions, effectiveRule, null);
|
||||
if (dependents.Contains(Dependents.Parents))
|
||||
{
|
||||
if (ruleVersions[dependency.Item1.Rule()] != dependency.Item1.Version())
|
||||
BitSet parents = relations.parents[dependency.Item1.Rule()];
|
||||
for (int parent = parents.NextSetBit(0); parent >= 0; parent = parents.NextSetBit
|
||||
(parent + 1))
|
||||
{
|
||||
incompatible.Append(string.Format("Element %s dependent on rule %s@%d (found @%d) in %s\n"
|
||||
, dependency.Item2.ToString(), ruleNames[dependency.Item1.Rule()], dependency
|
||||
.Item1.Version(), ruleVersions[dependency.Item1.Rule()], dependency.Item1.Recognizer
|
||||
().Name));
|
||||
if (parent < 0 || parent >= ruleVersions.Length || checked.Get(parent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
checked.Set(parent);
|
||||
int required = CheckDependencyVersion(errors, dependency, ruleNames, ruleVersions
|
||||
, parent, "parent");
|
||||
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
|
||||
}
|
||||
}
|
||||
if (dependents.Contains(Dependents.Children))
|
||||
{
|
||||
BitSet children = relations.children[dependency.Item1.Rule()];
|
||||
for (int child = children.NextSetBit(0); child >= 0; child = children.NextSetBit(
|
||||
child + 1))
|
||||
{
|
||||
if (child < 0 || child >= ruleVersions.Length || checked.Get(child))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
checked.Set(child);
|
||||
int required = CheckDependencyVersion(errors, dependency, ruleNames, ruleVersions
|
||||
, child, "child");
|
||||
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
|
||||
}
|
||||
}
|
||||
if (dependents.Contains(Dependents.Ancestors))
|
||||
{
|
||||
BitSet ancestors = relations.GetAncestors(dependency.Item1.Rule());
|
||||
for (int ancestor = ancestors.NextSetBit(0); ancestor >= 0; ancestor = ancestors.
|
||||
NextSetBit(ancestor + 1))
|
||||
{
|
||||
if (ancestor < 0 || ancestor >= ruleVersions.Length || checked.Get(ancestor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
checked.Set(ancestor);
|
||||
int required = CheckDependencyVersion(errors, dependency, ruleNames, ruleVersions
|
||||
, ancestor, "ancestor");
|
||||
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
|
||||
}
|
||||
}
|
||||
if (dependents.Contains(Dependents.Descendants))
|
||||
{
|
||||
BitSet descendants = relations.GetDescendants(dependency.Item1.Rule());
|
||||
for (int descendant = descendants.NextSetBit(0); descendant >= 0; descendant = descendants
|
||||
.NextSetBit(descendant + 1))
|
||||
{
|
||||
if (descendant < 0 || descendant >= ruleVersions.Length || checked.Get(descendant
|
||||
))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
checked.Set(descendant);
|
||||
int required = CheckDependencyVersion(errors, dependency, ruleNames, ruleVersions
|
||||
, descendant, "descendant");
|
||||
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
|
||||
}
|
||||
}
|
||||
int declaredVersion = dependency.Item1.Version();
|
||||
if (declaredVersion > highestRequiredDependency)
|
||||
{
|
||||
string message = string.Format("Rule dependency version mismatch: %s has maximum dependency version %d (expected %d) in %s%n"
|
||||
, ruleNames[dependency.Item1.Rule()], highestRequiredDependency, declaredVersion
|
||||
, dependency.Item1.Recognizer().ToString());
|
||||
errors.Append(message);
|
||||
}
|
||||
}
|
||||
if (incompatible.Length != 0)
|
||||
if (errors.Length > 0)
|
||||
{
|
||||
throw new InvalidOperationException(incompatible.ToString());
|
||||
throw new InvalidOperationException(errors.ToString());
|
||||
}
|
||||
MarkChecked(recognizerClass);
|
||||
MarkChecked(recognizerType);
|
||||
}
|
||||
|
||||
private static readonly ISet<Dependents> ImplementedDependents = EnumSet.Of(Dependents
|
||||
.Self, Dependents.Parents, Dependents.Children, Dependents.Ancestors, Dependents
|
||||
.Descendants);
|
||||
|
||||
private static void ReportUnimplementedDependents(StringBuilder errors, Tuple<RuleDependency
|
||||
, IAnnotatedElement> dependency, EnumSet<Dependents> dependents)
|
||||
{
|
||||
EnumSet<Dependents> unimplemented = dependents.Clone();
|
||||
unimplemented.RemoveAll(ImplementedDependents);
|
||||
if (!unimplemented.IsEmpty())
|
||||
{
|
||||
string message = string.Format("Cannot validate the following dependents of rule %d: %s%n"
|
||||
, dependency.Item1.Rule(), unimplemented);
|
||||
errors.Append(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static int CheckDependencyVersion(StringBuilder errors, Tuple<RuleDependency
|
||||
, IAnnotatedElement> dependency, string[] ruleNames, int[] ruleVersions, int
|
||||
relatedRule, string relation)
|
||||
{
|
||||
string ruleName = ruleNames[dependency.Item1.Rule()];
|
||||
string path;
|
||||
if (relation == null)
|
||||
{
|
||||
path = ruleName;
|
||||
}
|
||||
else
|
||||
{
|
||||
string mismatchedRuleName = ruleNames[relatedRule];
|
||||
path = string.Format("rule %s (%s of %s)", mismatchedRuleName, relation, ruleName
|
||||
);
|
||||
}
|
||||
int declaredVersion = dependency.Item1.Version();
|
||||
int actualVersion = ruleVersions[relatedRule];
|
||||
if (actualVersion > declaredVersion)
|
||||
{
|
||||
string message = string.Format("Rule dependency version mismatch: %s has version %d (expected <= %d) in %s%n"
|
||||
, path, actualVersion, declaredVersion, dependency.Item1.Recognizer().ToString
|
||||
());
|
||||
errors.Append(message);
|
||||
}
|
||||
return actualVersion;
|
||||
}
|
||||
|
||||
private static int[] GetRuleVersions<_T0>(Type<_T0> recognizerClass, string[] ruleNames
|
||||
|
@ -320,6 +479,148 @@ namespace Antlr4.Runtime.Misc
|
|||
}
|
||||
}
|
||||
|
||||
private static RuleDependencyChecker.RuleRelations ExtractRuleRelations<_T0>(Type
|
||||
<_T0> recognizer) where _T0:Recognizer<object, object>
|
||||
{
|
||||
string serializedATN = GetSerializedATN(recognizer);
|
||||
if (serializedATN == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ATN atn = ATNSimulator.Deserialize(serializedATN.ToCharArray());
|
||||
RuleDependencyChecker.RuleRelations relations = new RuleDependencyChecker.RuleRelations
|
||||
(atn.ruleToStartState.Length);
|
||||
foreach (ATNState state in atn.states)
|
||||
{
|
||||
if (!state.epsilonOnlyTransitions)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (Transition transition in state.GetTransitions())
|
||||
{
|
||||
if (transition.TransitionType != TransitionType.Rule)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
RuleTransition ruleTransition = (RuleTransition)transition;
|
||||
relations.AddRuleInvocation(state.ruleIndex, ruleTransition.target.ruleIndex);
|
||||
}
|
||||
}
|
||||
return relations;
|
||||
}
|
||||
|
||||
private static string GetSerializedATN<_T0>(Type<_T0> recognizerClass)
|
||||
{
|
||||
try
|
||||
{
|
||||
FieldInfo serializedAtnField = Sharpen.Runtime.GetDeclaredField(recognizerClass,
|
||||
"_serializedATN");
|
||||
if (Modifier.IsStatic(serializedAtnField.GetModifiers()))
|
||||
{
|
||||
return (string)serializedAtnField.GetValue(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (NoSuchFieldException)
|
||||
{
|
||||
if (recognizerClass.BaseType != null)
|
||||
{
|
||||
return GetSerializedATN(recognizerClass.BaseType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (SecurityException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (MemberAccessException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RuleRelations
|
||||
{
|
||||
private readonly BitSet[] parents;
|
||||
|
||||
private readonly BitSet[] children;
|
||||
|
||||
public RuleRelations(int ruleCount)
|
||||
{
|
||||
parents = new BitSet[ruleCount];
|
||||
for (int i = 0; i < ruleCount; i++)
|
||||
{
|
||||
parents[i] = new BitSet();
|
||||
}
|
||||
children = new BitSet[ruleCount];
|
||||
for (int i_1 = 0; i_1 < ruleCount; i_1++)
|
||||
{
|
||||
children[i_1] = new BitSet();
|
||||
}
|
||||
}
|
||||
|
||||
public bool AddRuleInvocation(int caller, int callee)
|
||||
{
|
||||
if (caller < 0)
|
||||
{
|
||||
// tokens rule
|
||||
return false;
|
||||
}
|
||||
if (children[caller].Get(callee))
|
||||
{
|
||||
// already added
|
||||
return false;
|
||||
}
|
||||
children[caller].Set(callee);
|
||||
parents[callee].Set(caller);
|
||||
return true;
|
||||
}
|
||||
|
||||
public BitSet GetAncestors(int rule)
|
||||
{
|
||||
BitSet ancestors = new BitSet();
|
||||
ancestors.Or(parents[rule]);
|
||||
while (true)
|
||||
{
|
||||
int cardinality = ancestors.Cardinality();
|
||||
for (int i = ancestors.NextSetBit(0); i >= 0; i = ancestors.NextSetBit(i + 1))
|
||||
{
|
||||
ancestors.Or(parents[i]);
|
||||
}
|
||||
if (ancestors.Cardinality() == cardinality)
|
||||
{
|
||||
// nothing changed
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
public BitSet GetDescendants(int rule)
|
||||
{
|
||||
BitSet descendants = new BitSet();
|
||||
descendants.Or(children[rule]);
|
||||
while (true)
|
||||
{
|
||||
int cardinality = descendants.Cardinality();
|
||||
for (int i = descendants.NextSetBit(0); i >= 0; i = descendants.NextSetBit(i + 1))
|
||||
{
|
||||
descendants.Or(children[i]);
|
||||
}
|
||||
if (descendants.Cardinality() == cardinality)
|
||||
{
|
||||
// nothing changed
|
||||
break;
|
||||
}
|
||||
}
|
||||
return descendants;
|
||||
}
|
||||
}
|
||||
|
||||
private RuleDependencyChecker()
|
||||
{
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue