Skip to content

Commit

Permalink
Initial draft for AnyText grammar
Browse files Browse the repository at this point in the history
  • Loading branch information
georghinkel committed Oct 2, 2024
1 parent 071a686 commit c180f2a
Show file tree
Hide file tree
Showing 42 changed files with 1,457 additions and 78 deletions.
15 changes: 13 additions & 2 deletions NMF.sln
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3A13497C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModelServicesTests", "Services\Tests\ModelServicesTests\ModelServicesTests.csproj", "{B62AF5A1-EA15-4226-8C22-193BCEDA198F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyText", "UserInterfaces\AnyText\AnyText.csproj", "{59BBC967-A1D3-4215-8111-8D7033CEECE8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnyText.Core", "UserInterfaces\AnyText.Core\AnyText.Core.csproj", "{59BBC967-A1D3-4215-8111-8D7033CEECE8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyText.Tests", "UserInterfaces\Tests\AnyText.Tests\AnyText.Tests.csproj", "{343546B3-D9C3-47E5-8927-961097F6BC75}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnyText.Tests", "UserInterfaces\Tests\AnyText.Tests\AnyText.Tests.csproj", "{343546B3-D9C3-47E5-8927-961097F6BC75}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyText", "UserInterfaces\AnyText\AnyText.csproj", "{DFBF717D-006D-48C3-9864-E494B8878346}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -780,6 +782,14 @@ Global
{343546B3-D9C3-47E5-8927-961097F6BC75}.Signed Debug|Any CPU.Build.0 = Debug|Any CPU
{343546B3-D9C3-47E5-8927-961097F6BC75}.Signed Release|Any CPU.ActiveCfg = Release|Any CPU
{343546B3-D9C3-47E5-8927-961097F6BC75}.Signed Release|Any CPU.Build.0 = Release|Any CPU
{DFBF717D-006D-48C3-9864-E494B8878346}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DFBF717D-006D-48C3-9864-E494B8878346}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFBF717D-006D-48C3-9864-E494B8878346}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFBF717D-006D-48C3-9864-E494B8878346}.Release|Any CPU.Build.0 = Release|Any CPU
{DFBF717D-006D-48C3-9864-E494B8878346}.Signed Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DFBF717D-006D-48C3-9864-E494B8878346}.Signed Debug|Any CPU.Build.0 = Debug|Any CPU
{DFBF717D-006D-48C3-9864-E494B8878346}.Signed Release|Any CPU.ActiveCfg = Release|Any CPU
{DFBF717D-006D-48C3-9864-E494B8878346}.Signed Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -864,6 +874,7 @@ Global
{B62AF5A1-EA15-4226-8C22-193BCEDA198F} = {3A13497C-33A7-4658-B206-1C1989D562FB}
{59BBC967-A1D3-4215-8111-8D7033CEECE8} = {6B587373-3C4C-4127-BFD1-DA9A89C0555B}
{343546B3-D9C3-47E5-8927-961097F6BC75} = {891A597A-A94C-43C7-BC65-3472326015DA}
{DFBF717D-006D-48C3-9864-E494B8878346} = {6B587373-3C4C-4127-BFD1-DA9A89C0555B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7B6D2737-94B7-470E-B413-D9693622AA4D}
Expand Down
17 changes: 17 additions & 0 deletions UserInterfaces/AnyText.Core/AnyText.Core.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputType>Library</OutputType>
<AssemblyName>NMF.AnyText.Core</AssemblyName>
<RootNamespace>NMF.AnyText</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<OutputPath>..\..\Build</OutputPath>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\Transformations\Transformations\Transformations.csproj" />
</ItemGroup>

</Project>
39 changes: 39 additions & 0 deletions UserInterfaces/AnyText.Core/Grammar/GrammarContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using NMF.AnyText.Rules;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NMF.AnyText.Grammar
{
public class GrammarContext
{
private readonly IDictionary<Type, Rule> _rules;
private readonly Dictionary<string, LiteralRule> _keywords = new Dictionary<string, LiteralRule>();

public GrammarContext(IDictionary<Type, Rule> rules)
{
_rules = rules;
}

public T ResolveRule<T>() where T : Rule
{
if (_rules.TryGetValue(typeof(T), out var rule))
{
return rule as T;
}
return null;
}

public LiteralRule ResolveKeyword(string keyword)
{
if (!_keywords.TryGetValue(keyword, out var rule))
{
rule = new LiteralRule(keyword);
_keywords.Add(keyword, rule);
}
return rule;
}
}
}
56 changes: 56 additions & 0 deletions UserInterfaces/AnyText.Core/Grammar/ReflectiveGrammar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using NMF.AnyText.Rules;
using NMF.Transformations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NMF.AnyText.Grammar
{
public abstract class ReflectiveGrammar
{
private GrammarContext _context;

public T GetRule<T>() where T : Rule
{
Initialize();
return _context?.ResolveRule<T>();
}

public void Initialize()
{
if (_context == null)
{
var rules = Reflector.ReflectDictionary(GetGrammarTypeStack(), CreateDefaultRules, CreateCustomRules);
_context = new GrammarContext(rules);
foreach (var rule in rules.Values)
{
rule.Initialize(_context);
}
}
}

private Stack<Type> GetGrammarTypeStack()
{
var typeStack = new Stack<Type>();
var currentType = this.GetType();
while (currentType != typeof(ReflectiveGrammar))
{
typeStack.Push(currentType);
currentType = currentType.BaseType;
}
return typeStack;
}

protected virtual IEnumerable<Rule> CreateDefaultRules()
{
return null;
}

protected virtual IEnumerable<Rule> CreateCustomRules()
{
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ internal RuleApplication MatchCore(Rule rule, ParseContext context, ref ParsePos
if (ruleApplication == null)
{
var col = position.Col;
ruleApplication = rule.Match(context, ref position);
if (!(ruleApplications is List<RuleApplication> ruleApplicationList))
{
// need to check again because another call could have created the column
Expand All @@ -52,7 +51,11 @@ internal RuleApplication MatchCore(Rule rule, ParseContext context, ref ParsePos
line.Columns.Add(col, ruleApplicationList);
}
}
var cycleDetector = new FailedRuleApplication(rule, default, position, "Cycle detected");
ruleApplicationList.Add(cycleDetector);
ruleApplication = rule.Match(context, ref position);
line.MaxExaminedLength = ParsePositionDelta.Larger(line.MaxExaminedLength, PrependColOffset(ruleApplication.ExaminedTo, col));
ruleApplicationList.Remove(cycleDetector);
ruleApplicationList.Add(ruleApplication);
}
else
Expand Down
138 changes: 138 additions & 0 deletions UserInterfaces/AnyText.Core/Model/AddAssignReferenceRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using NMF.AnyText.Rules;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NMF.AnyText.Model
{
/// <summary>
/// Denotes a rule that adds the value of an inner rule to a collection of the semantic element
/// </summary>
/// <typeparam name="TSemanticElement">The type of the context element</typeparam>
/// <typeparam name="TReference">The type of the property value</typeparam>
public abstract class AddAssignReferenceRule<TSemanticElement, TReference> : QuoteRule
{
/// <inheritdoc />
protected internal override void OnActivate(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TSemanticElement contextElement)
{
var resolveString = RuleHelper.Stringify(application.GetValue(context));
if (context.TryResolveReference<TReference>(contextElement, resolveString, out var propertyValue))
{
GetCollection(contextElement, context).Add(propertyValue);
}
else
{
context.EnqueueResolveAction(new ResolveAction(application, resolveString, default, true));
}
}
}

/// <inheritdoc />
protected internal override void OnDeactivate(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TSemanticElement contextElement)
{
var resolveString = RuleHelper.Stringify(application.GetValue(context));
if (context.TryResolveReference<TReference>(contextElement, resolveString, out var propertyValue))
{
GetCollection(contextElement, context).Remove(propertyValue);
}
else
{
context.EnqueueResolveAction(new ResolveAction(application, resolveString, default, false));
}
}
}

/// <inheritdoc />
protected internal override bool OnValueChange(RuleApplication application, ParseContext context)
{
// value change already handled in rule application
return application.ContextElement is TSemanticElement;
}

/// <inheritdoc />
protected override RuleApplication CreateRuleApplication(RuleApplication app, ParseContext context)
{
return new Application(this, app, app.Length, app.ExaminedTo);
}


/// <summary>
/// Obtains the child collection
/// </summary>
/// <param name="semanticElement">the semantic element</param>
/// <param name="context">the parse context in which the collection is obtained</param>
/// <returns>a collection of values</returns>
public abstract ICollection<TReference> GetCollection(TSemanticElement semanticElement, ParseContext context);

private sealed class Application : SingleRuleApplication
{
public Application(Rule rule, RuleApplication inner, ParsePositionDelta endsAt, ParsePositionDelta examinedTo) : base(rule, inner, endsAt, examinedTo)
{
}

protected override void OnMigrate(RuleApplication oldValue, RuleApplication newValue, ParseContext context)
{
if (oldValue.IsActive)
{
oldValue.Deactivate(context);
newValue.Activate(context);
if (Rule is AddAssignRule<TSemanticElement, TReference> addAssignRule && ContextElement is TSemanticElement contextElement)
{
var collection = addAssignRule.GetCollection(contextElement, context);
if (oldValue.GetValue(context) is TReference oldProperty)
{
collection.Remove(oldProperty);
}
if (newValue.GetValue(context) is TReference newProperty)
{
collection.Add(newProperty);
}
}
else
{
Rule.OnValueChange(this, context);
}
}
}
}


private sealed class ResolveAction : ParseResolveAction
{
private readonly bool _isAdd;

public ResolveAction(RuleApplication ruleApplication, string resolveString, ParsePosition position, bool isAdd) : base(ruleApplication, resolveString, position)
{
_isAdd = isAdd;
}

public override void OnParsingComplete(ParseContext parseContext)
{
var contextElement = RuleApplication.ContextElement;
if (parseContext.TryResolveReference<TReference>(contextElement, ResolveString, out var reference))
{
var parent = (AddAssignReferenceRule<TSemanticElement, TReference>)(RuleApplication.Rule);
var collection = parent.GetCollection((TSemanticElement)contextElement, parseContext);
if (_isAdd)
{
collection.Add(reference);
}
else
{
collection.Remove(reference);
}
}
else
{
parseContext.Errors.Add(new ParseError(Position, $"Could not resolve '{ResolveString}' as {typeof(TReference).Name}"));
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ namespace NMF.AnyText.Model
/// <summary>
/// Denotes a rule that adds the value of an inner rule to a collection of the semantic element
/// </summary>
/// <typeparam name="TContext">The type of the context element</typeparam>
/// <typeparam name="TSemanticElement">The type of the context element</typeparam>
/// <typeparam name="TProperty">The type of the property value</typeparam>
public abstract class AddAssignRule<TContext, TProperty> : QuoteRule
public abstract class AddAssignRule<TSemanticElement, TProperty> : QuoteRule
{
/// <inheritdoc />
protected internal override void OnActivate(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TContext contextElement && application.GetValue(context) is TProperty propertyValue)
if (application.ContextElement is TSemanticElement contextElement && application.GetValue(context) is TProperty propertyValue)
{
GetCollection(contextElement, context).Add(propertyValue);
}
Expand All @@ -26,7 +26,7 @@ protected internal override void OnActivate(RuleApplication application, ParseCo
/// <inheritdoc />
protected internal override void OnDeactivate(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TContext contextElement && application.GetValue(context) is TProperty propertyValue)
if (application.ContextElement is TSemanticElement contextElement && application.GetValue(context) is TProperty propertyValue)
{
GetCollection(contextElement, context).Remove(propertyValue);
}
Expand All @@ -35,12 +35,14 @@ protected internal override void OnDeactivate(RuleApplication application, Parse
/// <inheritdoc />
protected internal override bool OnValueChange(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TContext contextElement && application.GetValue(context) is TProperty propertyValue)
{
GetCollection(contextElement, context).Add(propertyValue);
return true;
}
return false;
// value change already handled in rule application
return application.ContextElement is TSemanticElement;
}

/// <inheritdoc />
protected override RuleApplication CreateRuleApplication(RuleApplication app, ParseContext context)
{
return new Application(this, app, app.Length, app.ExaminedTo);
}


Expand All @@ -50,11 +52,11 @@ protected internal override bool OnValueChange(RuleApplication application, Pars
/// <param name="semanticElement">the semantic element</param>
/// <param name="context">the parse context in which the collection is obtained</param>
/// <returns>a collection of values</returns>
public abstract ICollection<TProperty> GetCollection(TContext semanticElement, ParseContext context);
public abstract ICollection<TProperty> GetCollection(TSemanticElement semanticElement, ParseContext context);

private sealed class AddAssignRuleApplication : SingleRuleApplication
private sealed class Application : SingleRuleApplication
{
public AddAssignRuleApplication(Rule rule, RuleApplication inner, ParsePositionDelta endsAt, ParsePositionDelta examinedTo) : base(rule, inner, endsAt, examinedTo)
public Application(Rule rule, RuleApplication inner, ParsePositionDelta endsAt, ParsePositionDelta examinedTo) : base(rule, inner, endsAt, examinedTo)
{
}

Expand All @@ -64,7 +66,7 @@ protected override void OnMigrate(RuleApplication oldValue, RuleApplication newV
{
oldValue.Deactivate(context);
newValue.Activate(context);
if (Rule is AddAssignRule<TContext, TProperty> addAssignRule && ContextElement is TContext contextElement)
if (Rule is AddAssignRule<TSemanticElement, TProperty> addAssignRule && ContextElement is TSemanticElement contextElement)
{
var collection = addAssignRule.GetCollection(contextElement, context);
if (oldValue.GetValue(context) is TProperty oldProperty)
Expand Down
Loading

0 comments on commit c180f2a

Please sign in to comment.