diff --git a/src/libraries/Microsoft.PowerFx.Core/Public/Reflection/FunctionInfoSignature.cs b/src/libraries/Microsoft.PowerFx.Core/Public/Reflection/FunctionInfoSignature.cs index 4ac4d935c4..8546fb464a 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Public/Reflection/FunctionInfoSignature.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Public/Reflection/FunctionInfoSignature.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Text; using Microsoft.PowerFx.Core.Localization; +using Microsoft.PowerFx.Types; namespace Microsoft.PowerFx { @@ -52,6 +53,7 @@ public ParameterInfoSignature[] GetParameters(CultureInfo culture = null) List result = new List(); + int idx = 0; foreach (var p in _paramNames) { string unalterableName = p(localeName); @@ -60,16 +62,28 @@ public ParameterInfoSignature[] GetParameters(CultureInfo culture = null) _parent._fnc.TryGetParamDescription(invariantParamName, out var description, localeName); + FormulaType paramType = null; + if (_parent._fnc.TryGetTypeForArgSuggestionAt(idx, out var paramDType)) + { + paramType = FormulaType.Build(paramDType); + } + result.Add(new ParameterInfoSignature { Name = unalterableName, - Description = description + Description = description, + ParameterType = paramType }); + + idx++; } return result.ToArray(); } + // Return type of this. + public FormulaType ReturnType => FormulaType.Build(_parent._fnc.ReturnType); + // Keep this debug-only, since there are too many possible formats to pick just one. internal string DebugToString(CultureInfo culture = null) { @@ -78,15 +92,29 @@ internal string DebugToString(CultureInfo culture = null) sb.Append('('); var sep = string.Empty; - foreach (var param in GetParameterNames(culture)) + foreach (var param in GetParameters(culture)) { sb.Append(sep); - sb.Append(param); + sb.Append(param.Name); + + if (param.ParameterType != null) + { + sb.Append(": "); + sb.Append(param.ParameterType); + } + sep = ", "; } sb.Append(')'); + var retType = this.ReturnType; + if (retType != null) + { + sb.Append(": "); + sb.Append(retType); + } + return sb.ToString(); } diff --git a/src/libraries/Microsoft.PowerFx.Core/Public/Reflection/ParameterInfoSignature.cs b/src/libraries/Microsoft.PowerFx.Core/Public/Reflection/ParameterInfoSignature.cs index f479bb1b31..8d9d050c3b 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Public/Reflection/ParameterInfoSignature.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Public/Reflection/ParameterInfoSignature.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Diagnostics; +using Microsoft.PowerFx.Types; namespace Microsoft.PowerFx { @@ -15,5 +16,8 @@ public class ParameterInfoSignature public string Description { get; init; } // More fields, like optional? default value?, etc. + + // Could be null if not available. + public FormulaType ParameterType { get; init; } } } diff --git a/src/libraries/Microsoft.PowerFx.Interpreter/Functions/Library.cs b/src/libraries/Microsoft.PowerFx.Interpreter/Functions/Library.cs index ab580f2dd0..57f0180e0c 100644 --- a/src/libraries/Microsoft.PowerFx.Interpreter/Functions/Library.cs +++ b/src/libraries/Microsoft.PowerFx.Interpreter/Functions/Library.cs @@ -2763,7 +2763,7 @@ public static async ValueTask TraceFunction(EvalVisitor runner, Ev if (tracer == null) { - return FormulaValue.New(false); + return irContext.ResultType._type.Kind == DKind.Boolean ? FormulaValue.New(false) : FormulaValue.NewVoid(); } // the null case here handles Blanks diff --git a/src/libraries/Microsoft.PowerFx.Repl/Modules/FileLoader.cs b/src/libraries/Microsoft.PowerFx.Repl/Modules/FileLoader.cs index 6293ed0735..9fe34c015a 100644 --- a/src/libraries/Microsoft.PowerFx.Repl/Modules/FileLoader.cs +++ b/src/libraries/Microsoft.PowerFx.Repl/Modules/FileLoader.cs @@ -49,6 +49,12 @@ public FileLoader(string root) .Build(); var modulePoco = deserializer.Deserialize(txt); + if (modulePoco == null) + { + // This can happen on an empty file. + throw new InvalidOperationException($"Can't parse ModulePoco"); + } + modulePoco.Src_Filename = fullPath; return (modulePoco, this); diff --git a/src/libraries/Microsoft.PowerFx.Repl/Modules/FileName.cs b/src/libraries/Microsoft.PowerFx.Repl/Modules/FileName.cs new file mode 100644 index 0000000000..3130b43b68 --- /dev/null +++ b/src/libraries/Microsoft.PowerFx.Repl/Modules/FileName.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.PowerFx.Repl +{ + public static class ModuleHelper + { + // $$$ Potentially confusing - this does not actually update engine symbols... + public static async Task LoadModuleAsync(this RecalcEngine engine, string fullpath) + { + var commonIncomingSymbols = engine.GetCombinedEngineSymbols(); + var module = await LoadModuleAsync(fullpath, commonIncomingSymbols) + .ConfigureAwait(false); + + return module; + } + + public static async Task LoadModuleAsync(string fullpath, ReadOnlySymbolTable commonIncomingSymbols) + { + if (!Path.IsPathRooted(fullpath)) + { + throw new ArgumentException($"Path must be rooted"); + } + + var ctx = new ModuleLoadContext(commonIncomingSymbols); + + var errors = new List(); + + var module = await ctx.LoadFromFileAsync(fullpath, errors).ConfigureAwait(false); + var hasErrors = errors.Where(error => !error.IsWarning).Any(); + + if (hasErrors) + { + throw new InvalidOperationException($"Module has error"); + } + + return module; + } + } +} diff --git a/src/libraries/Microsoft.PowerFx.Repl/Modules/Module.cs b/src/libraries/Microsoft.PowerFx.Repl/Modules/Module.cs index f80f568c87..4d1f033c5a 100644 --- a/src/libraries/Microsoft.PowerFx.Repl/Modules/Module.cs +++ b/src/libraries/Microsoft.PowerFx.Repl/Modules/Module.cs @@ -2,10 +2,12 @@ // Licensed under the MIT license. using System; +using System.Collections.Generic; +using System.Linq; namespace Microsoft.PowerFx.Repl { - internal class Module + public class Module { /// /// Public symbols exported by this module. @@ -15,7 +17,7 @@ internal class Module /// /// Identity of the module. We should never have two different modules with the same identity. /// - public ModuleIdentity Identity { get; init; } + internal ModuleIdentity Identity { get; init; } /// /// Optional Full path that this module was loaded from. Null if not loaded from a file. @@ -27,6 +29,19 @@ internal Module(ModuleIdentity identity, ReadOnlySymbolTable exports) { this.Identity = identity; this.Symbols = exports ?? throw new ArgumentNullException(nameof(exports)); - } + } + + public IEnumerable PublicFunctions + { + get + { + // No other way to enumeraet them all +#pragma warning disable CS0618 // Type or member is obsolete + IEnumerable funcs = this.Symbols.Functions.Functions; +#pragma warning restore CS0618 // Type or member is obsolete + + return funcs.Select(func => new FunctionInfo(func)); + } + } } } diff --git a/src/tests/Microsoft.PowerFx.Repl.Tests.Shared/Microsoft.PowerFx.Repl.Tests.Shared.projitems b/src/tests/Microsoft.PowerFx.Repl.Tests.Shared/Microsoft.PowerFx.Repl.Tests.Shared.projitems index 71b973926e..1247169979 100644 --- a/src/tests/Microsoft.PowerFx.Repl.Tests.Shared/Microsoft.PowerFx.Repl.Tests.Shared.projitems +++ b/src/tests/Microsoft.PowerFx.Repl.Tests.Shared/Microsoft.PowerFx.Repl.Tests.Shared.projitems @@ -25,5 +25,6 @@ Always + \ No newline at end of file diff --git a/src/tests/Microsoft.PowerFx.Repl.Tests.Shared/Modules/table1.fx.yml b/src/tests/Microsoft.PowerFx.Repl.Tests.Shared/Modules/table1.fx.yml new file mode 100644 index 0000000000..a253986061 --- /dev/null +++ b/src/tests/Microsoft.PowerFx.Repl.Tests.Shared/Modules/table1.fx.yml @@ -0,0 +1,8 @@ +# Show example using a table. +Formulas: | + Point := Type({x:Number, y: Number}); + Dist(pt : Point) : Number = Sqrt(pt.x * pt.x + pt.y * pt.y); + + PointList := Type([Point]); + ToStr(t: PointList) : Text = Concat(t, $"({x},{y})", ","); + D2(t: PointList) : Number = Sqrt(Sum(t, ThisRecord.x* ThisRecord.x + ThisRecord.y * ThisRecord.y)); diff --git a/src/tools/Repl/Program.cs b/src/tools/Repl/Program.cs index cf17b1d33b..8fa49cedea 100644 --- a/src/tools/Repl/Program.cs +++ b/src/tools/Repl/Program.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.PowerFx.Core; +using Microsoft.PowerFx.Logging; using Microsoft.PowerFx.Repl; using Microsoft.PowerFx.Repl.Functions; using Microsoft.PowerFx.Repl.Services; @@ -165,7 +166,7 @@ public static void Main() #pragma warning disable CS0618 // Hook repl engine with customizations. - private class MyRepl : PowerFxREPL + private class MyRepl : PowerFxREPL, ITracer { public int _promptNumber = 1; @@ -181,6 +182,7 @@ public MyRepl() var bsp = new BasicServiceProvider(); bsp.AddService(_cultureInfo); + bsp.AddService(this); this.InnerServices = bsp; this.AllowSetDefinitions = true; @@ -207,6 +209,13 @@ public override async Task OnEvalExceptionAsync(Exception e, CancellationToken c Console.ResetColor(); } + + async Task ITracer.LogAsync(string message, TraceSeverity serv, RecordValue customRecord, CancellationToken ct) + { + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine($"{serv}: {message}"); + Console.ResetColor(); + } } public static void REPL(TextReader input, bool prompt, bool echo, bool printResult, uint? lineNumber)