Skip to content

DYN-5965: introduce new string from nodes that accept numeric format specifiers #14369

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
38a0567
fix most memory leaks from opening and closing Dynamo Splash Screen (…
mjkkirschner Sep 1, 2023
bd9071a
Merge branch 'master' of https://github.com/DynamoDS/Dynamo
mjkkirschner Sep 1, 2023
06e24bf
works and old graphs find method
mjkkirschner Sep 1, 2023
7a1a344
try to get tests passing
mjkkirschner Sep 5, 2023
bd25c84
fix default values
mjkkirschner Sep 5, 2023
17de8d6
remove duplicated builtin function endpoint
mjkkirschner Sep 5, 2023
6026229
save status
mjkkirschner Sep 5, 2023
22fcb33
merge conflict
mjkkirschner Oct 29, 2024
f981a14
builtin method tests are passing
mjkkirschner Oct 30, 2024
8fe9ebe
Merge branch 'master' of https://github.com/DynamoDS/Dynamo into code…
mjkkirschner Oct 31, 2024
dcc9d41
update graph tests
mjkkirschner Oct 31, 2024
45753f0
new builtin tests
mjkkirschner Oct 31, 2024
74a0b94
fix node doc tests
mjkkirschner Oct 31, 2024
91b7147
test dynpref format
mjkkirschner Nov 1, 2024
c21ba95
check for warning state
mjkkirschner Nov 1, 2024
8236398
add experimental flag to typeload data for node models
mjkkirschner Nov 4, 2024
ad3ff1b
fix docs tests
mjkkirschner Nov 5, 2024
f90f12e
update node names from review comments
mjkkirschner Nov 5, 2024
6d6f588
docs
mjkkirschner Nov 6, 2024
2f0692f
add experimental glyph on nodeview
mjkkirschner Nov 6, 2024
4f7e142
this works to set experimental namespaces as experimetnal
mjkkirschner Nov 7, 2024
81d0400
add feature flag check for ui
mjkkirschner Nov 7, 2024
a877951
api
mjkkirschner Nov 8, 2024
d11ed9f
update tests
mjkkirschner Nov 8, 2024
0e73d61
conflict
mjkkirschner Nov 8, 2024
e3d33e8
review comments
mjkkirschner Nov 11, 2024
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
94 changes: 82 additions & 12 deletions src/Engine/ProtoCore/DSASM/Mirror/ExecutionMirror.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using ProtoCore.Lang;
Expand All @@ -20,7 +21,7 @@ public SymbolNotFoundException(string symbolName)
public string SymbolName { get; private set; }
}

//Status: Draft, experiment
//Status: Draft, experiment :)

/// <summary>
/// Provides reflective capabilities over the execution of a DSASM Executable
Expand All @@ -44,24 +45,91 @@ public ExecutionMirror(ProtoCore.DSASM.Executive exec, ProtoCore.RuntimeCore cor
MirrorTarget = exec;
}

/// <summary>
///
/// </summary>
/// <param name="val"></param>
/// <param name="heap"></param>
/// <param name="langblock"></param>
/// <param name="forPrint"></param>
/// <param name="formatSpecifier"></param>
/// <returns></returns>
public string GetStringValueUsingFormat(StackValue val,string formatSpecifier, Heap heap, int langblock, bool forPrint = false)
{
return GetStringValueImplementation(val,formatSpecifier, heap, langblock, -1, -1, forPrint);
}

/// <summary>
///
/// </summary>
/// <param name="val"></param>
/// <param name="heap"></param>
/// <param name="langblock"></param>
/// <param name="forPrint"></param>
/// <returns></returns>
[Obsolete]
//TODO this is used all over the place internally
//so it's good to keep it and likely make it internal
//until we want to change formatting everywhere we process strings.
public string GetStringValue(StackValue val, Heap heap, int langblock, bool forPrint = false)
{
return GetStringValue(val, heap, langblock, -1, -1, forPrint);
}

/// <summary>
///
/// </summary>
/// <param name="val"></param>
/// <param name="heap"></param>
/// <param name="langblock"></param>
/// <param name="maxArraySize"></param>
/// <param name="maxOutputDepth"></param>
/// <param name="forPrint"></param>
/// <returns></returns>
[Obsolete]
public string GetStringValue(StackValue val, Heap heap, int langblock, int maxArraySize, int maxOutputDepth, bool forPrint = false)
{
if (formatParams == null)
formatParams = new OutputFormatParameters(maxArraySize, maxOutputDepth);
//use F6 to match the existing behavior.
return GetStringValueImplementation(val, StringUtils.LEGACYFORMATTING, heap, langblock, maxArraySize, maxOutputDepth, forPrint);
}

private string GetStringValueImplementation(StackValue val, string formatSpecifier, Heap heap, int langblock, int maxArraySize,
int maxOutputDepth, bool forPrint = false)
{
var legacyBehavior = formatSpecifier == StringUtils.LEGACYFORMATTING;
formatParams ??= new OutputFormatParameters(maxArraySize, maxOutputDepth);
//if format is the dynamo format specifier then use prefs format.
if (formatSpecifier == StringUtils.DynamoPreferencesNumberFormat)
{
formatSpecifier = ProtoCore.Mirror.MirrorData.PrecisionFormat;
}


if (val.IsInteger)
{
return val.IntegerValue.ToString();
//in legacy mode we don't format ints.
if (legacyBehavior)
{
return val.IntegerValue.ToString();
}
//TODO I am not sure if it's best to just return null or some message here
//or if we should catch the error higher up where we can log a runtime warning.
//currently we are doing the latter.

//return val.IntegerValue.SafeToStringWithFormat(formatSpecifier);
return val.IntegerValue.ToString(formatSpecifier);

}

if (val.IsDouble)
{
return val.DoubleValue.ToString("F6");
//we always use f6 for doubles in legacy mode.
if (legacyBehavior)
{
return val.DoubleValue.ToString("F6");
}
//return val.DoubleValue.SafeToStringWithFormat(formatSpecifier);
return val.DoubleValue.ToString(formatSpecifier);
}

if (val.IsNull)
Expand All @@ -77,7 +145,7 @@ public string GetStringValue(StackValue val, Heap heap, int langblock, int maxAr
if (val.IsArray)
{
HashSet<int> pointers = new HashSet<int> { val.ArrayPointer };
string arrTrace = GetArrayTrace(val, heap, langblock, pointers, forPrint);
string arrTrace = GetArrayTrace(val, formatSpecifier, heap, langblock, pointers, forPrint);
if (forPrint)
return "[" + arrTrace + "]";

Expand Down Expand Up @@ -125,6 +193,8 @@ public string GetStringValue(StackValue val, Heap heap, int langblock, int maxAr
return "null"; // "Value not yet supported for tracing";
}

//TODO - use format specifier when converting to string
//obsolete this method and introduce another.
public string GetClassTrace(StackValue val, Heap heap, int langblock, bool forPrint)
{
if (!formatParams.ContinueOutputTrace())
Expand Down Expand Up @@ -212,7 +282,7 @@ public string GetClassTrace(StackValue val, Heap heap, int langblock, bool forPr
}
}

private string GetPointerTrace(StackValue ptr, Heap heap, int langblock, HashSet<int> pointers, bool forPrint)
private string GetPointerTrace(StackValue ptr, string formatSpecifier, Heap heap, int langblock, HashSet<int> pointers, bool forPrint)
{
if (pointers.Contains(ptr.ArrayPointer))
{
Expand All @@ -223,13 +293,13 @@ private string GetPointerTrace(StackValue ptr, Heap heap, int langblock, HashSet

if (forPrint)
{
return "[" + GetArrayTrace(ptr, heap, langblock, pointers, forPrint) + "]";
return "[" + GetArrayTrace(ptr,formatSpecifier, heap, langblock, pointers, forPrint) + "]";
}

return "[ " + GetArrayTrace(ptr, heap, langblock, pointers, forPrint) + " ]";
return "[ " + GetArrayTrace(ptr,formatSpecifier, heap, langblock, pointers, forPrint) + " ]";
}

private string GetArrayTrace(StackValue svArray, Heap heap, int langblock, HashSet<int> pointers, bool forPrint)
private string GetArrayTrace(StackValue svArray, string formatSpecifier, Heap heap, int langblock, HashSet<int> pointers, bool forPrint)
{
if (!formatParams.ContinueOutputTrace())
return "...";
Expand Down Expand Up @@ -264,11 +334,11 @@ private string GetArrayTrace(StackValue svArray, Heap heap, int langblock, HashS
StackValue sv = array.GetValueFromIndex(n, runtimeCore);
if (sv.IsArray)
{
arrayElements.Append(GetPointerTrace(sv, heap, langblock, pointers, forPrint));
arrayElements.Append(GetPointerTrace(sv,formatSpecifier, heap, langblock, pointers, forPrint));
}
else
{
arrayElements.Append(GetStringValue(array.GetValueFromIndex(n, runtimeCore), heap, langblock, forPrint));
arrayElements.Append(GetStringValueUsingFormat(array.GetValueFromIndex(n, runtimeCore),formatSpecifier, heap, langblock, forPrint));
}

// If we need to truncate this array (halfArraySize > 0), and we have
Expand Down
24 changes: 21 additions & 3 deletions src/Engine/ProtoCore/Lang/BuiltInFunctionEndPoint.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -373,8 +373,26 @@ public override StackValue Execute(ProtoCore.Runtime.Context c, List<StackValue>
case BuiltInMethods.MethodID.ToString:
case BuiltInMethods.MethodID.ToStringFromObject:
case BuiltInMethods.MethodID.ToStringFromArray:
ret = StringUtils.ConvertToString(formalParameters[0], runtimeCore, rmem);
break;
{
ret = StringUtils.ConvertToString(formalParameters[0], runtimeCore, rmem);
break;
}
case BuiltInMethods.MethodID.ToStringFromObjectAndFormat:
case BuiltInMethods.MethodID.ToStringFromArrayAndFormat:
{
try
{
ret = StringUtils.ConvertToString(formalParameters, runtimeCore, rmem);
}
catch(System.FormatException fe)
{
runtimeCore.RuntimeStatus.LogWarning(WarningID.InvalidArguments, fe.Message);
//TODO reuse this string instead of allocating a new one each time?
//the message could be different...
ret = StackValue.BuildString(fe.Message,runtimeCore.Heap);
}
break;
}
case BuiltInMethods.MethodID.ImportData:
ret = ContextDataBuiltIns.ImportData(formalParameters[0], formalParameters[1], runtimeCore, interpreter, c);
break;
Expand Down
42 changes: 39 additions & 3 deletions src/Engine/ProtoCore/Lang/BuiltInMethods.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using ProtoCore.AST.AssociativeAST;
using ProtoCore.DSASM;
Expand Down Expand Up @@ -69,8 +69,13 @@ public enum MethodID
NodeAstFailed,
GC,
ConditionalIf,
ToStringFromObjectAndFormat,
ToStringFromArrayAndFormat,
}

//this array gets accessed using the MethodID enum
//so its order is important... could be a dictionary or attributes on the enums
//to avoid confusion, easy to mess this up.
private static string[] methodNames = new string[]
{
"AllFalse", // kAllFalse
Expand Down Expand Up @@ -130,6 +135,8 @@ public enum MethodID
Constants.kNodeAstFailed, // kNodeAstFailed
"__GC", // kGC
Constants.kIfConditionalMethodName,
"__ToStringFromObjectAndFormat", // kToStringFromObjectAndFormat
"__ToStringFromArrayAndFormat", // kToStringFromArrayAndFormat
};

public static string GetMethodName(MethodID id)
Expand Down Expand Up @@ -797,6 +804,7 @@ public BuiltInMethods(Core core)
new KeyValuePair<string, ProtoCore.Type>("object", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.Var, 0)),
},
ID = BuiltInMethods.MethodID.ToStringFromObject,
MethodAttributes = new MethodAttributes(true, false, @"This method is obsolete, please use 'ToStringFromObjectAndFormat' "),
},

new BuiltInMethod
Expand All @@ -807,7 +815,35 @@ public BuiltInMethods(Core core)
{
new KeyValuePair<string, Type>("list", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.Var)),
}.ToList(),
ID = BuiltInMethods.MethodID.ToStringFromArray
ID = BuiltInMethods.MethodID.ToStringFromArray,
MethodAttributes = new MethodAttributes(true, false, @"This method is obsolete, please use 'ToStringFromArrayAndFormat' "),
},

new BuiltInMethod
{
ReturnType = TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.String, 0),
Parameters = new List<KeyValuePair<string, ProtoCore.Type>>
{
new KeyValuePair<string, ProtoCore.Type>("object", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.Var, 0)),
new KeyValuePair<string, ProtoCore.Type>("formatSpecifier", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.String, 0)),
//TODO (MJK)
//if we wanted to support default args for builtins we would need to support these names being parsed as binary expressions instead
//of just identifiers as is done today. We would need to add a new Parameters entry because all of this is public :(
//or we could invoke the parser directly on these strings as is done for custom node symbols...
},
ID = BuiltInMethods.MethodID.ToStringFromObjectAndFormat,
},

new BuiltInMethod
{
ReturnType = TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.String, 0),

Parameters = new []
{
new KeyValuePair<string, Type>("list", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.Var)),
new KeyValuePair<string, ProtoCore.Type>("formatSpecifier", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.String, 0)),
}.ToList(),
ID = BuiltInMethods.MethodID.ToStringFromArrayAndFormat
},

new BuiltInMethod
Expand Down Expand Up @@ -915,4 +951,4 @@ public BuiltInMethods(Core core)
};
}
}
}
}
59 changes: 57 additions & 2 deletions src/Engine/ProtoCore/Utils/StringUtils.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
using ProtoCore.DSASM;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace ProtoCore.Utils
{
public static class StringUtils
{
internal const string DynamoPreferencesNumberFormat = nameof(DynamoPreferencesNumberFormat);
internal const string LEGACYFORMATTING = nameof(LEGACYFORMATTING);

public static int CompareString(StackValue s1, StackValue s2, RuntimeCore runtimeCore)
{
if (!s1.IsString || !s2.IsString)
Expand All @@ -20,21 +24,72 @@ public static int CompareString(StackValue s1, StackValue s2, RuntimeCore runtim
return string.Compare(str1, str2);
}

/// <summary>
/// Wraps ToString(format) in a try catch. Will return empty string if format is not valid for the target type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="target"></param>
/// <param name="Format"></param>
/// <returns>Will return empty string if format is not valid for the target type.</returns>
internal static string SafeToStringWithFormat<T>(this T target, string Format)
where T : IFormattable
{
try
{
return target.ToString(Format, null);
}
catch
{
return string.Empty;
}
}

public static string GetStringValue(StackValue sv, RuntimeCore runtimeCore)
{
ProtoCore.DSASM.Mirror.ExecutionMirror mirror = new DSASM.Mirror.ExecutionMirror(new ProtoCore.DSASM.Executive(runtimeCore), runtimeCore);
return mirror.GetStringValue(sv, runtimeCore.RuntimeMemory.Heap, 0, true);
}

//used by legacy ToString methods without format specifier.
public static StackValue ConvertToString(StackValue sv, RuntimeCore runtimeCore, ProtoCore.Runtime.RuntimeMemory rmem)
{
//maintain old behavior of existing string conversion nodes by passing null for formatSpecifier.
return ConvertToStringInternal(sv, runtimeCore, null);
}
//used by new ToString methods with format specifier.
internal static StackValue ConvertToString(IEnumerable<StackValue> args, RuntimeCore runtimeCore, ProtoCore.Runtime.RuntimeMemory rmem)
{
//TODO dislike this as a default...
var formatSpecifier = DynamoPreferencesNumberFormat;
var sv = args.ElementAt(0);
if (args.Count() > 1)
{ //TODO performance concern?
formatSpecifier = GetStringValue(args.ElementAt(1),runtimeCore);
}
return ConvertToStringInternal(sv, runtimeCore, formatSpecifier);
}

private static StackValue ConvertToStringInternal(StackValue sv, RuntimeCore runtimeCore, string formatSpecifier)
{
StackValue returnSV;
//TODO: Change Execution mirror class to have static methods, so that an instance does not have to be created
ProtoCore.DSASM.Mirror.ExecutionMirror mirror = new DSASM.Mirror.ExecutionMirror(new ProtoCore.DSASM.Executive(runtimeCore), runtimeCore);
returnSV = ProtoCore.DSASM.StackValue.BuildString(mirror.GetStringValue(sv, runtimeCore.RuntimeMemory.Heap, 0, true), runtimeCore.RuntimeMemory.Heap);
ProtoCore.DSASM.Mirror.ExecutionMirror mirror =
new DSASM.Mirror.ExecutionMirror(new ProtoCore.DSASM.Executive(runtimeCore), runtimeCore);
if (formatSpecifier == null)
{
returnSV = ProtoCore.DSASM.StackValue.BuildString(
mirror.GetStringValue(sv, runtimeCore.RuntimeMemory.Heap, 0, true), runtimeCore.RuntimeMemory.Heap);
}
else
{
returnSV = ProtoCore.DSASM.StackValue.BuildString(
mirror.GetStringValueUsingFormat(sv, formatSpecifier, runtimeCore.RuntimeMemory.Heap, 0, true), runtimeCore.RuntimeMemory.Heap);
}
return returnSV;
}



public static StackValue ConcatString(StackValue op1, StackValue op2, RuntimeCore runtimeCore)
{
var v1 = runtimeCore.RuntimeMemory.Heap.ToHeapObject<DSString>(op1).Value;
Expand Down
Loading
Loading