Skip to content

Commit

Permalink
first draft for anyText
Browse files Browse the repository at this point in the history
  • Loading branch information
georghinkel committed Oct 1, 2024
1 parent bb570e5 commit 071a686
Show file tree
Hide file tree
Showing 30 changed files with 1,909 additions and 1 deletion.
24 changes: 23 additions & 1 deletion NMF.sln
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PropertyService", "Services
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3A13497C-33A7-4658-B206-1C1989D562FB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModelServicesTests", "Services\Tests\ModelServicesTests\ModelServicesTests.csproj", "{B62AF5A1-EA15-4226-8C22-193BCEDA198F}"
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}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyText.Tests", "UserInterfaces\Tests\AnyText.Tests\AnyText.Tests.csproj", "{343546B3-D9C3-47E5-8927-961097F6BC75}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -760,6 +764,22 @@ Global
{B62AF5A1-EA15-4226-8C22-193BCEDA198F}.Signed Debug|Any CPU.Build.0 = Debug|Any CPU
{B62AF5A1-EA15-4226-8C22-193BCEDA198F}.Signed Release|Any CPU.ActiveCfg = Release|Any CPU
{B62AF5A1-EA15-4226-8C22-193BCEDA198F}.Signed Release|Any CPU.Build.0 = Release|Any CPU
{59BBC967-A1D3-4215-8111-8D7033CEECE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{59BBC967-A1D3-4215-8111-8D7033CEECE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{59BBC967-A1D3-4215-8111-8D7033CEECE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{59BBC967-A1D3-4215-8111-8D7033CEECE8}.Release|Any CPU.Build.0 = Release|Any CPU
{59BBC967-A1D3-4215-8111-8D7033CEECE8}.Signed Debug|Any CPU.ActiveCfg = Debug|Any CPU
{59BBC967-A1D3-4215-8111-8D7033CEECE8}.Signed Debug|Any CPU.Build.0 = Debug|Any CPU
{59BBC967-A1D3-4215-8111-8D7033CEECE8}.Signed Release|Any CPU.ActiveCfg = Release|Any CPU
{59BBC967-A1D3-4215-8111-8D7033CEECE8}.Signed Release|Any CPU.Build.0 = Release|Any CPU
{343546B3-D9C3-47E5-8927-961097F6BC75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{343546B3-D9C3-47E5-8927-961097F6BC75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{343546B3-D9C3-47E5-8927-961097F6BC75}.Release|Any CPU.ActiveCfg = Release|Any CPU
{343546B3-D9C3-47E5-8927-961097F6BC75}.Release|Any CPU.Build.0 = Release|Any CPU
{343546B3-D9C3-47E5-8927-961097F6BC75}.Signed Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -842,6 +862,8 @@ Global
{B40B7DC2-CFA8-4C1A-8289-ADF7507D651D} = {1E8C8464-ACBC-4201-9F38-FFBA1959A860}
{3A13497C-33A7-4658-B206-1C1989D562FB} = {1E8C8464-ACBC-4201-9F38-FFBA1959A860}
{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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7B6D2737-94B7-470E-B413-D9693622AA4D}
Expand Down
13 changes: 13 additions & 0 deletions UserInterfaces/AnyText/AnyText.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

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

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

namespace NMF.AnyText
{
/// <summary>
/// Denotes a class that matches text using an incremental packrat parser
/// </summary>
public class Matcher
{
private readonly List<MemoLine> _memoTable = new List<MemoLine>();

private MemoLine GetLine(int line)
{
while (_memoTable.Count <= line)
{
_memoTable.Add(new MemoLine());
}
return _memoTable[line];
}

private static IEnumerable<RuleApplication> GetCol(SortedDictionary<int, List<RuleApplication>> line, int col)
{
if (line.TryGetValue(col, out var ruleApplications))
{
return ruleApplications;
}
return Enumerable.Empty<RuleApplication>();
}

internal RuleApplication MatchCore(Rule rule, ParseContext context, ref ParsePosition position)
{
var line = GetLine(position.Line);
var ruleApplications = GetCol(line.Columns, position.Col);

var ruleApplication = ruleApplications.FirstOrDefault(r => r.Rule == rule);
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
ruleApplicationList = GetCol(line.Columns, col) as List<RuleApplication>;
if (ruleApplicationList == null)
{
ruleApplicationList = new List<RuleApplication>();
line.Columns.Add(col, ruleApplicationList);
}
}
line.MaxExaminedLength = ParsePositionDelta.Larger(line.MaxExaminedLength, PrependColOffset(ruleApplication.ExaminedTo, col));
ruleApplicationList.Add(ruleApplication);
}
else
{
position += ruleApplication.Length;
}
if (rule.TrailingWhitespaces)
{
RuleHelper.MoveOverWhitespace(context, ref position);
}
return ruleApplication;
}

private static ParsePositionDelta PrependColOffset(ParsePositionDelta examinedTo, int col)
{
if (examinedTo.Line == 0)
{
return new ParsePositionDelta(0, examinedTo.Col + col);
}
return examinedTo;
}

/// <summary>
/// Matches the provided rule with the given parse context
/// </summary>
/// <param name="context">The context in which the text is parsed, including the current input</param>
/// <returns>A rule application for the entire text</returns>
public RuleApplication Match(ParseContext context)
{
var position = new ParsePosition(0, 0);
RuleHelper.MoveOverWhitespace(context, ref position);
var match = MatchCore(context.RootRule, context, ref position);
if (!match.IsPositive || position.Line == context.Input.Length)
{
return match;
}
return new FailedRuleApplication(context.RootRule, new ParsePositionDelta(position.Line, position.Col), position, "Unexpected content");
}

/// <summary>
/// Removes any memoization based on the given text edit
/// </summary>
/// <param name="edit">The change in the input text</param>
public void RemoveMemoizedRuleApplications(TextEdit edit)
{
for (int i = 0; i <= edit.Start.Line; i++)
{
var line = GetLine(i);

if (new ParsePosition(i, 0) + line.MaxExaminedLength < edit.Start)
{
continue;
}

var maxReach = default(ParsePositionDelta);

foreach (var col in line.Columns)
{
var pos = new ParsePosition(i, col.Key);

for (int j = col.Value.Count - 1; j >= 0; j--)
{
var ruleApplication = col.Value[j];
if (pos + ruleApplication.ExaminedTo < edit.Start)
{
maxReach = ParsePositionDelta.Larger(maxReach, PrependColOffset(ruleApplication.ExaminedTo, col.Key));
}
else
{
col.Value.RemoveAt(j);
}
}
}

line.MaxExaminedLength = maxReach;
}
}

private sealed class MemoLine
{
public SortedDictionary<int, List<RuleApplication>> Columns { get; } = new SortedDictionary<int, List<RuleApplication>>();

public ParsePositionDelta MaxExaminedLength { get; set; }
}
}
}
87 changes: 87 additions & 0 deletions UserInterfaces/AnyText/Model/AddAssignRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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="TContext">The type of the context element</typeparam>
/// <typeparam name="TProperty">The type of the property value</typeparam>
public abstract class AddAssignRule<TContext, TProperty> : QuoteRule
{
/// <inheritdoc />
protected internal override void OnActivate(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TContext contextElement && application.GetValue(context) is TProperty propertyValue)
{
GetCollection(contextElement, context).Add(propertyValue);
}
}

/// <inheritdoc />
protected internal override void OnDeactivate(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TContext contextElement && application.GetValue(context) is TProperty propertyValue)
{
GetCollection(contextElement, context).Remove(propertyValue);
}
}

/// <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;
}


/// <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<TProperty> GetCollection(TContext semanticElement, ParseContext context);

private sealed class AddAssignRuleApplication : SingleRuleApplication
{
public AddAssignRuleApplication(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<TContext, TProperty> addAssignRule && ContextElement is TContext contextElement)
{
var collection = addAssignRule.GetCollection(contextElement, context);
if (oldValue.GetValue(context) is TProperty oldProperty)
{
collection.Remove(oldProperty);
}
if (newValue.GetValue(context) is TProperty newProperty)
{
collection.Add(newProperty);
}
}
else
{
Rule.OnValueChange(this, context);
}
}
}
}
}
}
55 changes: 55 additions & 0 deletions UserInterfaces/AnyText/Model/AssignRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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 assigns the value of a child rule to a certain property
/// </summary>
/// <typeparam name="TContext">The type of the context element</typeparam>
/// <typeparam name="TProperty">The type of the property value</typeparam>
public abstract class AssignRule<TContext, TProperty> : QuoteRule
{
/// <inheritdoc />
protected internal override void OnActivate(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TContext contextElement && application.GetValue(context) is TProperty propertyValue)
{
OnChangeValue(contextElement, propertyValue, context);
}
}

/// <inheritdoc />
protected internal override void OnDeactivate(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TContext contextElement)
{
OnChangeValue(contextElement, default, context);
}
}

/// <inheritdoc />
protected internal override bool OnValueChange(RuleApplication application, ParseContext context)
{
if (application.ContextElement is TContext contextElement && application.GetValue(context) is TProperty propertyValue)
{
OnChangeValue(contextElement, propertyValue, context);
return true;
}
return false;
}

/// <summary>
/// Gets called when the value changes
/// </summary>
/// <param name="semanticElement">the context element</param>
/// <param name="propertyValue">the property value</param>
/// <param name="context">the parsing context</param>
protected abstract void OnChangeValue(TContext semanticElement, TProperty propertyValue, ParseContext context);

}
}
Loading

0 comments on commit 071a686

Please sign in to comment.