From de28bc391d59ded8bf1bfbb58c5e599de71f565e Mon Sep 17 00:00:00 2001 From: Quin Lynch Date: Sat, 27 Apr 2024 03:01:35 -0300 Subject: [PATCH] Handle optional function args better internally --- src/EdgeDB.Net.QueryBuilder/Grammar/Terms.cs | 34 +++++---- .../Lexical/QueryWriter.cs | 71 +++++++++++++------ src/EdgeDB.Net.QueryBuilder/Lexical/Value.cs | 12 +++- .../Translators/Methods/MethodTranslator.cs | 2 +- .../Methods/TranslatedParameter.cs | 3 + 5 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/EdgeDB.Net.QueryBuilder/Grammar/Terms.cs b/src/EdgeDB.Net.QueryBuilder/Grammar/Terms.cs index 1b9c8f8a..40769582 100644 --- a/src/EdgeDB.Net.QueryBuilder/Grammar/Terms.cs +++ b/src/EdgeDB.Net.QueryBuilder/Grammar/Terms.cs @@ -138,8 +138,17 @@ public static QueryWriter Function(this QueryWriter writer, string name, Deferra for (var i = 0; i < args.Length;) { + int commaPos = 0; + LooseLinkedList.Node? comma = null; + + if (i > 0) + { + commaPos = writer.TailIndex; + writer.Append(", ", out comma); + } + var arg = args[i++]; - bool isEmpty = false; + var isEmpty = false; writer.Marker( MarkerType.FunctionArg, @@ -148,24 +157,25 @@ public static QueryWriter Function(this QueryWriter writer, string name, Deferra Value.Of( writer => { - if (writer.AppendIsEmpty(arg.Value, out _, out var node)) + if (arg.Named is not null) { - isEmpty = true; - return; + writer.AppendPrefixIfNotEmpty( + Value.Of(writer => writer.Append(arg.Named, " := ")), + arg.Value, + out isEmpty + ); + } + else + { + writer.Append(arg.Value); } - - // append the named part if its specified - if (arg.Named is not null) - writer.Prepend(node, Value.Of(writer => writer.Append(arg.Named, " := "))); } ) ); - if(isEmpty) - continue; + if (!isEmpty || comma is null) continue; - if (i != args.Length) - writer.Append(", "); + writer.Remove(commaPos, comma); } writer.Append(')'); diff --git a/src/EdgeDB.Net.QueryBuilder/Lexical/QueryWriter.cs b/src/EdgeDB.Net.QueryBuilder/Lexical/QueryWriter.cs index b33fa091..b681e771 100644 --- a/src/EdgeDB.Net.QueryBuilder/Lexical/QueryWriter.cs +++ b/src/EdgeDB.Net.QueryBuilder/Lexical/QueryWriter.cs @@ -35,7 +35,7 @@ public void Dispose() private ValueNode? _track; private readonly bool _debug; - private int TailIndex => _tokens.Count - 1; + public int TailIndex => _tokens.Count - 1; public QueryWriter(bool debug = false) { @@ -74,8 +74,9 @@ private ValueNode AddTracked(in Value value, bool after) // track is already updated. return head ?? _track!; } - else if (_track is null) - _track = _tokens.AddFirst(in value); //Set(ref _tokens.AddFirst(in value)); + + if (_track is null) + _track = _tokens.AddFirst(in value); else { _track = after @@ -172,6 +173,23 @@ public QueryWriter Marker(MarkerType type, string name, Deferrable? debu public QueryWriter Remove(int position, ValueNode head, int count = 1) { + if (count == 0) + return this; + + if (count < 0) + { + count *= -1; + var countForCopy = count - 1; + + for (var i = 0; i < countForCopy; i++) + { + if (head.Previous is null) + count--; + else + head = head.Previous; + } + } + var headPrev = head.Previous; _tokens.Remove(head, count, (node) => @@ -197,32 +215,24 @@ public QueryWriter Replace(ValueNode node, int position, int size, in Value valu return this; } - public QueryWriter Prepend(ValueNode node, in Value value) + public QueryWriter Append(in Value value, out ValueNode node) { - var index = GetIndexOfNode(node); - - if (index == -1) - throw new InvalidOperationException("Node cannot be found in collection of tokens"); - - // change the track to the node - var oldTrack = _track; + node = AddAfterTracked(in value); - _track = node; - OnNodeAdd(AddBeforeTracked(in value)); - _track = oldTrack; + OnNodeAdd(node); - Markers.Update(index - 1, 1); + Markers.Update(TailIndex, 1); return this; } - public QueryWriter Append(in Value value, out ValueNode node) + public QueryWriter Append(in Value value, out ValueNode node, out int size) { - node = AddAfterTracked(in value); + var pos = TailIndex; - OnNodeAdd(node); + Append(in value, out node); - Markers.Update(TailIndex, 1); + size = TailIndex - pos; return this; } @@ -272,14 +282,35 @@ public bool AppendIsEmpty(in Value value) public bool AppendIsEmpty(in Value value, out int size) => AppendIsEmpty(in value, out size, out _); - public bool AppendIsEmpty(in Value value, out int size, out ValueNode node) + public bool AppendIsEmpty(in Value value, out int size, [MaybeNullWhen(true)] out ValueNode node) { + if (value == Value.Empty) + { + size = 0; + node = _track; + return true; + } + var index = TailIndex; Append(in value, out node); size = TailIndex - index; return size == 0; } + public QueryWriter AppendPrefixIfNotEmpty(in Value prefix, in Value value, out bool wasEmpty) + { + var pos = TailIndex; + Append(in prefix, out var prefixHead, out var prefixSize); + + if (AppendIsEmpty(in value)) + { + Remove(pos, prefixHead, prefixSize); + wasEmpty = true; + } + else wasEmpty = false; + return this; + } + public StringBuilder Compile(StringBuilder? builder = null) { builder ??= new StringBuilder(); diff --git a/src/EdgeDB.Net.QueryBuilder/Lexical/Value.cs b/src/EdgeDB.Net.QueryBuilder/Lexical/Value.cs index c9c80e1c..20a51635 100644 --- a/src/EdgeDB.Net.QueryBuilder/Lexical/Value.cs +++ b/src/EdgeDB.Net.QueryBuilder/Lexical/Value.cs @@ -6,7 +6,7 @@ namespace EdgeDB; [DebuggerDisplay("{DebugDisplay()}")] -internal readonly struct Value +internal readonly struct Value : IEquatable { [MemberNotNullWhen(false, nameof(_callback))] public bool IsScalar @@ -104,4 +104,14 @@ public override string ToString() public static implicit operator Value(WriterProxy callback) => new(callback); public static implicit operator Value(int v) => new(v.ToString()); public static implicit operator Value(long v) => new(v.ToString()); + + public bool Equals(Value other) => Equals(_callback, other._callback) && Equals(_value, other._value) && _str == other._str && _ch == other._ch; + + public override bool Equals(object? obj) => obj is Value other && Equals(other); + + public override int GetHashCode() => HashCode.Combine(_callback, _value, _str, _ch); + + public static bool operator ==(Value left, Value right) => left.Equals(right); + + public static bool operator !=(Value left, Value right) => !left.Equals(right); } diff --git a/src/EdgeDB.Net.QueryBuilder/Translators/Methods/MethodTranslator.cs b/src/EdgeDB.Net.QueryBuilder/Translators/Methods/MethodTranslator.cs index 6224f664..bc949488 100644 --- a/src/EdgeDB.Net.QueryBuilder/Translators/Methods/MethodTranslator.cs +++ b/src/EdgeDB.Net.QueryBuilder/Translators/Methods/MethodTranslator.cs @@ -133,7 +133,7 @@ public static void TranslateMethod(QueryWriter writer, MethodCallExpression meth /// protected Value OptionalArg(TranslatedParameter? arg) { - if (arg is null) + if (arg is null || arg.IsNullValue) return Value.Empty; else return arg; diff --git a/src/EdgeDB.Net.QueryBuilder/Translators/Methods/TranslatedParameter.cs b/src/EdgeDB.Net.QueryBuilder/Translators/Methods/TranslatedParameter.cs index 99a24e76..caf2234e 100644 --- a/src/EdgeDB.Net.QueryBuilder/Translators/Methods/TranslatedParameter.cs +++ b/src/EdgeDB.Net.QueryBuilder/Translators/Methods/TranslatedParameter.cs @@ -51,6 +51,9 @@ public bool IsLinkType public bool IsMultiLinkType => EdgeDBTypeUtils.IsLink(ParameterType, out var isMulti, out _) && isMulti; + public bool IsNullValue + => RawValue is ConstantExpression {Value: null}; + /// /// Constructs a new . ///