Skip to content

Commit

Permalink
Fixed bugs and tests to prep for release.
Browse files Browse the repository at this point in the history
  • Loading branch information
tnunnink committed Nov 28, 2023
1 parent c92a016 commit 4aaea93
Show file tree
Hide file tree
Showing 16 changed files with 118 additions and 112 deletions.
43 changes: 35 additions & 8 deletions src/.idea/.idea.L5Sharp/.idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 37 additions & 35 deletions src/L5Sharp/Common/Instruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using JetBrains.Annotations;
using L5Sharp.Utilities;

Expand Down Expand Up @@ -53,6 +52,11 @@ public sealed class Instruction
/// of an instruction signature into separate parsable values.
/// </summary>
private const string ArgumentSplitPattern = ",(?![^[]*])";

/// <summary>
/// Lazy list of all known instructions and their corresponding factory method function.
/// </summary>
private static Dictionary<string, Func<Instruction>> _known = Factories().ToDictionary(x => x.Key, x => x.Value);

/// <summary>
/// Creates a new <see cref="Instruction"/> with the provided string key and regex signature pattern.
Expand All @@ -68,16 +72,6 @@ private Instruction(string key, string? signature = null, params Argument[] argu
Key = key;
Signature = signature ?? $"{key}({DefaultArgs(arguments.Length)})";
Arguments = arguments;
Operadns = ExtractOperands(Signature);
}

private IEnumerable<string> ExtractOperands(string signature)
{
var input = Regex.Match(Signature, SignaturePattern).Value[1..^1];

return !string.IsNullOrEmpty(input)
? Regex.Split(input, ArgumentSplitPattern)
: Enumerable.Empty<string>().ToArray();
}

/// <summary>
Expand Down Expand Up @@ -110,7 +104,17 @@ private IEnumerable<string> ExtractOperands(string signature)
/// The collection of operand names found in the signature of the instruction.
/// </summary>
///
public IEnumerable<string> Operadns { get; }
public IEnumerable<string> Operadns
{
get
{
var input = Regex.Match(Signature, SignaturePattern).Value[1..^1];

return !string.IsNullOrEmpty(input)
? Regex.Split(input, ArgumentSplitPattern)
: Enumerable.Empty<string>().ToArray();
}
}

/// <summary>
/// The <see cref="NeutralText"/> representation of the instruction instance.
Expand Down Expand Up @@ -184,7 +188,7 @@ public static Instruction Parse(string text)
var signature = Regex.Match(text, SignaturePattern).Value[1..^1];
var arguments = Regex.Split(signature, ArgumentSplitPattern).Select(Argument.Parse).ToArray();

return _known.Value.TryGetValue(key, out var create)
return _known.TryGetValue(key, out var create)
? create().Of(arguments)
: new Instruction(key, arguments: arguments);
}
Expand All @@ -195,7 +199,7 @@ public static Instruction Parse(string text)
/// <returns>
/// A <see cref="IEnumerable{T}"/> containing all known <see cref="Instruction"/> instances with no arguments.
/// </returns>
public static IEnumerable<string> Keys() => _known.Value.Keys.AsEnumerable();
public static IEnumerable<string> Keys() => _known.Keys.AsEnumerable();

/// <summary>
/// Retrieves teh argument for a specified operand name from the instruction.
Expand All @@ -218,6 +222,24 @@ public static Instruction Parse(string text)
return index >= 0 && index < Arguments.Count() ? Arguments.ElementAt(index) : default;
}

/// <summary>
/// Retrieves all <see cref="TagName"/> values from this instruction instance's arguments.
/// </summary>
/// <returns>A collection of <see cref="TagName"/> values cotnained by the instruction.</returns>
public IEnumerable<TagName> Tags()
{
var tags = new List<TagName>();

foreach (var argument in Arguments)
{
if (argument.IsTag) tags.Add((TagName)argument);
if (!argument.IsExpression) continue;
tags.AddRange(((NeutralText)argument).Tags());
}

return tags;
}

/// <summary>
/// Creates a <see cref="Instruction"/> of the same type with the updated argument values.
/// </summary>
Expand Down Expand Up @@ -269,20 +291,6 @@ public override bool Equals(object? obj)
/// <returns><c>true</c> if the values are not equal; Otherwise, <c>false</c>.</returns>
public static bool operator !=(Instruction? left, Instruction? right) => !Equals(left, right);

/// <summary>
/// Implicitly converts the <see cref="Instruction"/> instance to a <see cref="string"/> value.
/// </summary>
/// <param name="instruction">The instruction to convert.</param>
/// <returns>A <see cref="string"/> representing the instrcution key.</returns>
public static implicit operator string(Instruction instruction) => instruction.Key;

/// <summary>
/// Explicitly converts the <see cref="string"/> value to an <see cref="Instruction"/> instance.
/// </summary>
/// <param name="key">The string key of the instrution.</param>
/// <returns></returns>
public static implicit operator Instruction(string key) => new(key, $"{key}()");

#region Factories

/// <summary>
Expand Down Expand Up @@ -1706,18 +1714,12 @@ private static string DefaultArgs(int number)
return string.Join(',', Enumerable.Range(0, number).Select(i => $"arg{i}"));
}

/// <summary>
/// Lazy list of all known instructions and their corresponding factory method function.
/// </summary>
private static Lazy<Dictionary<string, Func<Instruction>>> _known =>
new(() => GetFactories().ToDictionary(x => x.Key, x => x.Value), LazyThreadSafetyMode.ExecutionAndPublication);

/// <summary>
/// Indexes all instruction factory methods in the class and creates a function returning the instruction
/// mathcing the specified key or method name. The method is passed null argumnts and therefore will be a default
/// instruction instance. Callers can then use <see cref="Of"/> to pass argument array.
/// </summary>
private static IEnumerable<KeyValuePair<string, Func<Instruction>>> GetFactories()
private static IEnumerable<KeyValuePair<string, Func<Instruction>>> Factories()
{
var methods = typeof(Instruction).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.ReturnType == typeof(Instruction) && m.Name.All(char.IsUpper));
Expand Down
5 changes: 2 additions & 3 deletions src/L5Sharp/Common/NeutralText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,15 @@ public IEnumerable<Instruction> Instructions(Instruction instruction) =>
/// Gets a collection of tag names found in the current neutral text.
/// </summary>
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="TagName"/> values that were in from the current text.</returns>
/// <seealso cref="TagsIn(Instruction)"/>
/// <seealso cref="TagsIn(string)"/>
public IEnumerable<TagName> Tags() => Regex.Matches(_text, TagName.SearchPattern).Select(t => new TagName(t.Value));

/// <summary>
/// Gets a collection of tag names found in the current neutral text that are operands or arguments to a specific instruction.
/// </summary>
/// <param name="instruction">The instruction for which to find tags as arguments to.</param>
/// <returns>A <see cref="IEnumerable{T}"/> containing tag names found in the specified instruction.</returns>
public IEnumerable<TagName> TagsIn(Instruction instruction) =>
Instructions(instruction).SelectMany(i => i.Text.Tags());
public IEnumerable<TagName> TagsIn(string instruction) => Instructions(instruction).SelectMany(i => i.Text.Tags());

/// <inheritdoc />
public override string ToString() => _text;
Expand Down
7 changes: 3 additions & 4 deletions src/L5Sharp/Elements/Chart.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using L5Sharp.Common;
using L5Sharp.Utilities;
Expand Down Expand Up @@ -32,10 +33,8 @@ public Chart(XElement element) : base(element)
/// <inheritdoc />
public override int Number => 0;

public override IEnumerable<CrossReference> References()
{
throw new NotImplementedException();
}
/// <inheritdoc />
public override IEnumerable<CrossReference> References() => Enumerable.Empty<CrossReference>();

/// <inheritdoc />
protected override IEnumerable<string> Ordering()
Expand Down
16 changes: 9 additions & 7 deletions src/L5Sharp/Elements/Line.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,33 @@ public override IEnumerable<CrossReference> References()
{
var references = new List<CrossReference>();

foreach (var instruction in Text.Instructions())
var instructions = Text.Instructions().ToList();

foreach (var instruction in instructions)
{
references.Add(new CrossReference(Element, L5XName.Instruction, instruction.Key, instruction));
references.Add(new CrossReference(Element, L5XName.Instruction, instruction.Key));

if (instruction.IsRoutineCall)
{
var routine = instruction.Arguments.FirstOrDefault()?.ToString() ?? string.Empty;
references.Add(new CrossReference(Element, L5XName.Routine, routine, instruction));
references.Add(new CrossReference(Element, L5XName.Routine, routine, instruction.Key));

var parameters = instruction.Arguments.Skip(1).Where(a => a.IsTag).Select(t => t.ToString());
references.AddRange(parameters.Select(p => new CrossReference(Element, L5XName.Tag, p, instruction)));
references.AddRange(parameters.Select(p => new CrossReference(Element, L5XName.Tag, p, instruction.Key)));
continue;
}

if (instruction.IsTaskCall)
{
var task = instruction.Arguments.FirstOrDefault()?.ToString() ?? string.Empty;
references.Add(new CrossReference(Element, L5XName.Task, task, instruction));
references.Add(new CrossReference(Element, L5XName.Task, task, instruction.Key));
continue;
}

//todo other instructions like GSV SSV

references.AddRange(instruction.Text.Tags()
.Select(t => new CrossReference(Element, L5XName.Tag, t.ToString(), instruction)));
references.AddRange(instruction.Tags()
.Select(t => new CrossReference(Element, L5XName.Tag, t.ToString(), instruction.Key)));
}

return references;
Expand Down
Loading

0 comments on commit 4aaea93

Please sign in to comment.