Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/L5Sharp.Core/Code/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ public Block WireTo(TagName target, TagName? from = null)
if (target is null || target.IsEmpty)
throw new ArgumentException("Can not wire block with null or empty target tag name.");

var operand = target.Base;
var pin = target.Member;
var operand = target.BaseName;
var pin = target.MemberPath ?? TagName.Empty;

var to = Element.Parent?.Elements().FirstOrDefault(e =>
e.GetBlockOperand() == operand
Expand Down Expand Up @@ -256,8 +256,8 @@ public Block WireFrom(TagName source, TagName? to = null)
if (source is null || source.IsEmpty)
throw new ArgumentException("Can not wire block with null or empty target tag name.");

var operand = source.Base;
var pin = source.Member;
var operand = source.BaseName;
var pin = source.MemberPath ?? TagName.Empty;

var from = Element.Parent?.Elements().FirstOrDefault(e =>
e.GetBlockOperand() == operand
Expand Down Expand Up @@ -377,7 +377,7 @@ public Instruction ToInstruction()

builder.Append(")");

return Instruction.Parse(builder.ToString());
return new Instruction(builder.ToString());
}

/// <inheritdoc />
Expand Down Expand Up @@ -469,12 +469,12 @@ private static IEnumerable<TagName> GetBlockTags(XElement element)
{
var operand = element.GetBlockOperand();

if (operand.Type.IsInvalid) return [];
if (operand.IsValid) return [];

return element.Attributes()
.Where(a => PinNames.Contains(a.Name.LocalName))
.SelectMany(a => a.Value.Split(' '))
.Select(t => TagName.Concat(operand, t));
.Select(t => operand.ToTagName().Append(t));
}

/// <summary>
Expand Down Expand Up @@ -511,7 +511,7 @@ private static IEnumerable<TagName> GetBlockInputs(XElement element)
foreach (var wire in inputWires)
{
var operand = sheet.Elements().Single(e => e.Attribute(L5XName.ID)?.Value == wire.Id).GetBlockOperand();
tagNames.Add(TagName.Concat(operand, wire.Param ?? TagName.Empty));
tagNames.Add(operand.ToTagName().Append(wire.Param));
}

return tagNames;
Expand Down Expand Up @@ -543,7 +543,7 @@ private static IEnumerable<TagName> GetBlockOutputs(XElement element)
foreach (var wire in outputWires)
{
var operand = sheet.Elements().Single(e => e.Attribute(L5XName.ID)?.Value == wire.Id).GetBlockOperand();
tagNames.Add(TagName.Concat(operand, wire.Param ?? TagName.Empty));
tagNames.Add(operand.ToTagName().Append(wire.Param));
}

return tagNames;
Expand Down
6 changes: 4 additions & 2 deletions src/L5Sharp.Core/Code/Line.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ public Line(string text) : base(L5XName.Line)
/// <inheritdoc />
public override IEnumerable<Instruction> Instructions()
{
return Instruction.Split(Element.Value);
return Instruction.Parse(Element.Value);
}

/// <inheritdoc />
public override IEnumerable<TagName> Tags()
{
return Instruction.Split(Element.Value).SelectMany(x => x.Tags);
return Instruction.Parse(Element.Value).SelectMany(x =>
x.Arguments.Where(a => a.IsReference).Select(a => a.ToTagName())
);
}

/// <inheritdoc />
Expand Down
7 changes: 5 additions & 2 deletions src/L5Sharp.Core/Code/Rung.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,16 @@ public string? Comment
/// <inheritdoc />
public override IEnumerable<Instruction> Instructions()
{
return Instruction.Split(Text);
return Instruction.Parse(Text);
}

/// <inheritdoc />
public override IEnumerable<TagName> Tags()
{
return Instruction.Split(Text).SelectMany(x => x.Tags);

return Instruction.Parse(Text).SelectMany(x =>
x.Arguments.Where(a => a.IsReference).Select(a => a.ToTagName())
);
}

/// <inheritdoc />
Expand Down
15 changes: 9 additions & 6 deletions src/L5Sharp.Core/Code/Sheet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ public override IEnumerable<Instruction> Instructions()
/// <inheritdoc />
public override IEnumerable<TagName> Tags()
{
return Blocks().Select(b => b.ToInstruction()).SelectMany(i => i.Tags);
return Blocks().Select(b => b.ToInstruction()).SelectMany(x =>
x.Arguments.Where(a => a.IsReference).Select(a => a.ToTagName())
);
}

/// <inheritdoc />
Expand Down Expand Up @@ -291,16 +293,17 @@ public Sheet Connect(TagName from, TagName to)
if (from is null) throw new ArgumentNullException(nameof(from));
if (to is null) throw new ArgumentNullException(nameof(to));

var source = Element.Elements().SingleOrDefault(e => e.GetBlockOperand() == from.Base)?.Deserialize<Block>();
var target = Element.Elements().SingleOrDefault(e => e.GetBlockOperand() == to.Base)?.Deserialize<Block>();
var source = Element.Elements().SingleOrDefault(e => e.GetBlockOperand() == from.BaseName)
?.Deserialize<Block>();
var target = Element.Elements().SingleOrDefault(e => e.GetBlockOperand() == to.BaseName)?.Deserialize<Block>();

if (source is null)
throw new InvalidOperationException($"No source block with operand '{from.Base}' exists in the sheet.");
throw new InvalidOperationException($"No source block with operand '{from.BaseName}' exists in the sheet.");

if (target is null)
throw new InvalidOperationException($"No target block with operand '{to.Base}' exists in the sheet.");
throw new InvalidOperationException($"No target block with operand '{to.BaseName}' exists in the sheet.");

WireBlocks(source.ID, target.ID, from.Member, to.Member);
WireBlocks(source.ID, target.ID, from.MemberPath, to.MemberPath);

return this;
}
Expand Down
111 changes: 46 additions & 65 deletions src/L5Sharp.Core/Common/Argument.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System;
using System.Globalization;
using System.Linq;

namespace L5Sharp.Core;

Expand Down Expand Up @@ -42,26 +40,34 @@ public Argument(string value)
public ArgumentType Type => ArgumentType.Of(_value);

/// <summary>
/// The collection of <see cref="TagName"/> values found in the argument.
/// Gets a value indicating whether this argument is invalid (either empty or unknown).
/// </summary>
/// <value>A <see cref="IEnumerable{T}"/> of <see cref="TagName"/> values.</value>
/// <remarks>
/// Since an argument could represent a complex expression, it may contain more than one tag name value.
/// We need a way to get all tag names from a single argument, whether it's a single tag name or expression or
/// multiple tag names.
/// </remarks>
public IReadOnlyList<TagName> Tags => ExtractTags(_value).ToArray();
public bool IsValid => Type != ArgumentType.Empty && Type != ArgumentType.Unknown;

/// <summary>
/// The collection of <see cref="TagName"/> values found in the argument.
/// Gets a value indicating whether this argument represents an immediate value (atomic or string).
/// </summary>
/// <value>A <see cref="IEnumerable{T}"/> of <see cref="TagName"/> values.</value>
/// <remarks>
/// Since an argument could represent a complex expression, it may contain more than one tag name value.
/// We need a way to get all tag names from a single argument, whether it's a single tag name or expression or
/// multiple tag names.
/// </remarks>
public IReadOnlyList<AtomicData> Values => ExtractValues(_value).ToArray();
public bool IsLiteral => Type == ArgumentType.Atomic || Type == ArgumentType.String;

/// <summary>
/// Indicates whether the current argument is of type <see cref="ArgumentType.Reference"/>.
/// </summary>
public bool IsReference => Type == ArgumentType.Reference;

/// <summary>
/// Gets a value indicating whether this argument represents an atomic value.
/// </summary>
public bool IsAtomic => Type == ArgumentType.Atomic;

/// <summary>
/// Indicates whether the argument represents a string literal type.
/// </summary>
public bool IsString => Type == ArgumentType.String;

/// <summary>
/// Gets a value indicating whether this argument represents an expression containing operators.
/// </summary>
public bool IsExpression => Type == ArgumentType.Expression;

/// <summary>
/// Represents an unknown argument that can be found in certain instruction text.
Expand All @@ -77,7 +83,25 @@ public Argument(string value)
/// </remarks>
public static Argument Empty => new(string.Empty);

#region Equality
/// <summary>
/// Converts this argument to a <see cref="TagName"/> instance.
/// </summary>
/// <returns>A <see cref="TagName"/> representing the tag reference in this argument.</returns>
/// <exception cref="InvalidOperationException">Thrown when the argument type is not <see cref="ArgumentType.Reference"/>.</exception>
public TagName ToTagName() => new(_value);

/// <summary>
/// Converts this argument to an <see cref="AtomicData"/> instance by parsing its immediate atomic value.
/// </summary>
/// <returns>An <see cref="AtomicData"/> representing the parsed atomic value from this argument.</returns>
/// <exception cref="InvalidOperationException">Thrown when the argument type is not <see cref="ArgumentType.Atomic"/>.</exception>
public AtomicData ToAtomic() => AtomicData.Parse(_value);

/// <summary>
/// Converts the current <see cref="Argument"/> value to a <see cref="NeutralText"/> representation.
/// </summary>
/// <returns>A <see cref="NeutralText"/> instance containing the converted value of the current <see cref="Argument"/>.</returns>
public NeutralText ToNeutralText() => new(_value);

/// <inheritdoc />
public override bool Equals(object? obj) => _value.Equals(obj?.ToString());
Expand All @@ -104,10 +128,6 @@ public Argument(string value)
/// <returns>true if the left Argument is not equal to the right Argument; otherwise, false.</returns>
public static bool operator !=(Argument left, Argument right) => Equals(left, right);

#endregion

#region Operators

/// <summary>
/// Implicitly converts the provided <see cref="TagName"/> to an <see cref="Argument"/>.
/// </summary>
Expand Down Expand Up @@ -200,48 +220,9 @@ public Argument(string value)
public static implicit operator Argument(double value) => new(value.ToString(CultureInfo.InvariantCulture));

/// <summary>
/// Explicitly converts the provided <see cref="Argument"/> to a <see cref="TagName"/>.
/// Explicitly converts the provided <see cref="Argument"/> to a <see cref="string"/>.
/// </summary>
/// <param name="argument">The <see cref="Argument"/> object to convert.</param>
/// <returns>A <see cref="TagName"/> object representing the value of the argument.</returns>
/// <returns>A <see cref="string"/> object representing the value of the argument.</returns>
public static implicit operator string(Argument argument) => argument._value;

#endregion

#region Internal

/// <summary>
/// Extracts all tag names from the provided text based on a predefined search pattern.
/// </summary>
private static IEnumerable<TagName> ExtractTags(string argument)
{
var type = ArgumentType.Of(argument);

if (type == ArgumentType.Tag)
return [argument];

if (type == ArgumentType.Expression)
return TagName.Scrape(argument);

return [];
}

/// <summary>
/// Extracts a collection of <see cref="AtomicData"/> from the given argument string.
/// </summary>
private static IEnumerable<AtomicData> ExtractValues(string argument)
{
var type = ArgumentType.Of(argument);

if (type == ArgumentType.Atomic)
return [AtomicData.Parse(argument)];

if (type == ArgumentType.Expression)
//todo handle nested values in expression
return [];

return [];
}

#endregion
}
Loading
Loading