Skip to content

Commit

Permalink
#628 - Create CallExpression / High-Order Functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
sys27 committed May 14, 2023
1 parent 790899c commit 25b8178
Show file tree
Hide file tree
Showing 95 changed files with 1,485 additions and 923 deletions.
11 changes: 6 additions & 5 deletions xFunc Grammar.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// It's just a reference grammar for xFunc (The implementaion is not completely equal to grammar).

statement = unaryAssign
/ binaryAssign
/ assign
statement = assign
/ def
/ undef
/ if
Expand All @@ -26,7 +24,7 @@ binaryAssign = variable ('+=' / '-=' / '*=' / '/=' / '<<=' / '>>=') exp
ternary = conditional ('?' exp ':' exp)*

conditional = bitwise (('&&' / '||') bitwise)*
bitwise = equality (('&' / 'and' / '|' / 'or' / 'xor' / '=>' / '->' / 'impl' / '<=>' / '<->' / 'eq' / 'nor' / 'nand') equality)*
bitwise = equality (('&' / 'and' / '|' / 'or' / 'xor' / 'impl' / 'eq' / 'nor' / 'nand') equality)*
equality = shift (('==' / '!=' / '<' / '<=' / '>' / '>=') shift)*
shift = addSub (('<<' / '>>') addSub)*
addSub = mulDivMod (('+' / '-') mulDivMod)*
Expand All @@ -48,6 +46,7 @@ operand = complexnumber /
variable /
boolean /
bracketExp /
lambda /
matrix /
vector

Expand All @@ -70,4 +69,6 @@ parameters = (statement (',' statement)*)*
vector = ('{' / '(') parameters ('}' / ')')
matrix = ('{' / '(') vector (',' vector) ('}' / ')')

functionDeclaration = id '(' (variable (',' variable)* / '') ')'
functionDeclaration = id '(' (variable (',' variable)* / '') ')'

lambda = '(' (id (',' id) / '') ')' '=>' exp
1 change: 1 addition & 0 deletions xFunc.DotnetTool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using xFunc.DotnetTool.Options;
using xFunc.Maths.Expressions.Parameters;

namespace xFunc.DotnetTool;

Expand Down
2 changes: 1 addition & 1 deletion xFunc.DotnetTool/xFunc.DotnetTool.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<Using Include="xFunc.Maths.Analyzers" />
<Using Include="xFunc.Maths.Analyzers.TypeAnalyzers" />
<Using Include="xFunc.Maths.Expressions" />
<Using Include="xFunc.Maths.Expressions.Collections" />
<Using Include="xFunc.Maths.Expressions.Parameters" />
<Using Include="xFunc.Maths.Expressions.Matrices" />
<Using Include="xFunc.Maths.Expressions.Units" />
<Using Include="xFunc.Maths.Results" />
Expand Down
6 changes: 5 additions & 1 deletion xFunc.Maths/Analyzers/Analyzer{TResult,TContext}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ public virtual TResult Analyze(Undefine exp, TContext context)
=> Analyze(exp as IExpression, context);

/// <inheritdoc />
public virtual TResult Analyze(UserFunction exp, TContext context)
public virtual TResult Analyze(CallExpression exp, TContext context)
=> Analyze(exp as IExpression, context);

/// <inheritdoc />
public virtual TResult Analyze(LambdaExpression exp, TContext context)
=> Analyze(exp as IExpression, context);

/// <inheritdoc />
Expand Down
6 changes: 5 additions & 1 deletion xFunc.Maths/Analyzers/Analyzer{TResult}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,11 @@ public virtual TResult Analyze(Undefine exp)
=> Analyze(exp as IExpression);

/// <inheritdoc />
public virtual TResult Analyze(UserFunction exp)
public virtual TResult Analyze(CallExpression exp)
=> Analyze(exp as IExpression);

/// <inheritdoc />
public virtual TResult Analyze(LambdaExpression exp)
=> Analyze(exp as IExpression);

/// <inheritdoc />
Expand Down
11 changes: 0 additions & 11 deletions xFunc.Maths/Analyzers/Differentiator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,17 +371,6 @@ public override IExpression Analyze(UnaryMinus exp, DifferentiatorContext contex
return new UnaryMinus(exp.Argument.Analyze(this, context));
}

/// <inheritdoc />
public override IExpression Analyze(UserFunction exp, DifferentiatorContext context)
{
ValidateArguments(exp, context);

if (context.Parameters is null)
throw new InvalidOperationException();

return context.Parameters.Functions[exp].Analyze(this, context);
}

/// <inheritdoc />
public override IExpression Analyze(Variable exp, DifferentiatorContext context)
{
Expand Down
26 changes: 13 additions & 13 deletions xFunc.Maths/Analyzers/Formatters/CommonFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ namespace xFunc.Maths.Analyzers.Formatters;
/// <seealso cref="IFormatter" />
public class CommonFormatter : IFormatter
{
/// <summary>
/// Gets the instance of <see cref="CommonFormatter"/>.
/// </summary>
public static CommonFormatter Instance { get; } = new CommonFormatter();

private string ToString(UnaryExpression exp, string format)
{
var arg = exp.Argument.Analyze(this);
Expand Down Expand Up @@ -235,23 +240,18 @@ public virtual string Analyze(Undefine exp)
=> $"undef({exp.Key.Analyze(this)})";

/// <inheritdoc />
public virtual string Analyze(UserFunction exp)
public virtual string Analyze(CallExpression exp)
{
var sb = new StringBuilder();

sb.Append(exp.Function).Append('(');
if (exp.ParametersCount > 0)
{
foreach (var item in exp.Arguments)
sb.Append(item).Append(", ");
sb.Remove(sb.Length - 2, 2);
}

sb.Append(')');
if (exp.Function is LambdaExpression)
return $"({exp.Function})({string.Join(", ", exp.Parameters)})";

return sb.ToString();
return $"{exp.Function}({string.Join(", ", exp.Parameters)})";
}

/// <inheritdoc />
public virtual string Analyze(LambdaExpression exp)
=> $"{exp.Lambda}";

/// <inheritdoc />
public virtual string Analyze(Variable exp) => exp.Name;

Expand Down
10 changes: 9 additions & 1 deletion xFunc.Maths/Analyzers/IAnalyzer{TResult,TContext}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,15 @@ public interface IAnalyzer<out TResult, in TContext>
/// <param name="exp">The expression.</param>
/// <param name="context">The context.</param>
/// <returns>The result of analysis.</returns>
TResult Analyze(UserFunction exp, TContext context);
TResult Analyze(CallExpression exp, TContext context);

/// <summary>
/// Analyzes the specified expression.
/// </summary>
/// <param name="exp">The expression.</param>
/// <param name="context">The context.</param>
/// <returns>The result of analysis.</returns>
TResult Analyze(LambdaExpression exp, TContext context);

/// <summary>
/// Analyzes the specified expression.
Expand Down
9 changes: 8 additions & 1 deletion xFunc.Maths/Analyzers/IAnalyzer{TResult}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,14 @@ public interface IAnalyzer<out TResult>
/// </summary>
/// <param name="exp">The expression.</param>
/// <returns>The result of analysis.</returns>
TResult Analyze(UserFunction exp);
TResult Analyze(CallExpression exp);

/// <summary>
/// Analyzes the specified expression.
/// </summary>
/// <param name="exp">The expression.</param>
/// <returns>The result of analysis.</returns>
TResult Analyze(LambdaExpression exp);

/// <summary>
/// Analyzes the specified expression.
Expand Down
37 changes: 27 additions & 10 deletions xFunc.Maths/Analyzers/Simplifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -938,26 +938,43 @@ public override IExpression Analyze(UnaryMinus exp)
}

/// <inheritdoc />
public override IExpression Analyze(UserFunction exp)
public override IExpression Analyze(CallExpression exp)
{
if (exp is null)
ArgNull(ExceptionArgument.exp);

var arguments = exp.Arguments;
var isExpChanged = false;
var function = exp.Function.Analyze(this);
var isChanged = IsChanged(exp.Function, function);
var parameters = exp.Parameters;

for (var i = 0; i < exp.ParametersCount; i++)
for (var i = 0; i < exp.Parameters.Length; i++)
{
var expression = exp[i].Analyze(this);
if (IsChanged(exp[i], expression))
var parameter = exp.Parameters[i];
var simplified = parameter.Analyze(this);
if (IsChanged(parameter, simplified))
{
isExpChanged = true;
arguments = arguments.SetItem(i, expression);
parameters = parameters.SetItem(i, simplified);
isChanged = true;
}
}

if (isExpChanged)
return exp.Clone(arguments);
if (isChanged)
return exp.Clone(function, parameters);

return exp;
}

/// <inheritdoc />
public override IExpression Analyze(LambdaExpression exp)
{
if (exp is null)
ArgNull(ExceptionArgument.exp);

var body = exp.Lambda.Body.Analyze(this);
if (IsChanged(exp.Lambda.Body, body))
{
return exp.Clone(new Lambda(exp.Lambda.Parameters, body));
}

return exp;
}
Expand Down
5 changes: 5 additions & 0 deletions xFunc.Maths/Analyzers/TypeAnalyzers/ResultTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,9 @@ public enum ResultTypes
/// The expression returns a volume number.
/// </summary>
VolumeNumber = 1 << 15,

/// <summary>
/// The expression return a function.
/// </summary>
Function = 1 << 16,
}
6 changes: 5 additions & 1 deletion xFunc.Maths/Analyzers/TypeAnalyzers/TypeAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1311,9 +1311,13 @@ public virtual ResultTypes Analyze(Undefine exp)
=> CheckArgument(exp, ResultTypes.String);

/// <inheritdoc />
public virtual ResultTypes Analyze(UserFunction exp)
public virtual ResultTypes Analyze(CallExpression exp)
=> CheckArgument(exp, ResultTypes.Undefined);

/// <inheritdoc />
public virtual ResultTypes Analyze(LambdaExpression exp)
=> CheckArgument(exp, ResultTypes.Function);

/// <inheritdoc />
public virtual ResultTypes Analyze(Variable exp)
=> CheckArgument(exp, ResultTypes.Undefined);
Expand Down
2 changes: 1 addition & 1 deletion xFunc.Maths/Expressions/BinaryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public override bool Equals(object? obj)
public string ToString(IFormatter formatter) => Analyze(formatter);

/// <inheritdoc />
public override string ToString() => ToString(new CommonFormatter());
public override string ToString() => ToString(CommonFormatter.Instance);

/// <inheritdoc />
public object Execute() => Execute(null);
Expand Down
130 changes: 130 additions & 0 deletions xFunc.Maths/Expressions/CallExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) Dmytro Kyshchenko. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Immutable;

namespace xFunc.Maths.Expressions;

/// <summary>
/// Represent the expression to call function.
/// </summary>
/// <seealso cref="Function"/>
public class CallExpression : IExpression, IEquatable<CallExpression>
{
/// <summary>
/// Initializes a new instance of the <see cref="CallExpression"/> class.
/// </summary>
/// <param name="function">The expression that returns function.</param>
/// <param name="parameters">The list of parameters of the function.</param>
public CallExpression(IExpression function, ImmutableArray<IExpression> parameters)
{
Function = function;
Parameters = parameters;
}

/// <inheritdoc />
public bool Equals(CallExpression? other)
{
if (ReferenceEquals(null, other))
return false;

if (ReferenceEquals(this, other))
return true;

return Function.Equals(other.Function) && Parameters.SequenceEqual(other.Parameters);
}

/// <inheritdoc />
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;

if (ReferenceEquals(this, obj))
return true;

if (obj.GetType() != GetType())
return false;

return Equals((CallExpression)obj);
}

/// <inheritdoc />
/// <exception cref="NotSupportedException">Always.</exception>
public object Execute()
=> throw new NotSupportedException();

/// <inheritdoc />
public object Execute(ExpressionParameters? parameters)
{
if (parameters is null)
{
throw new ArgumentNullException(nameof(parameters));
}

if (Function.Execute(parameters) is not Lambda function)
{
throw new ResultIsNotSupportedException(this, Function);
}

var nestedScope = parameters.CreateScope();
var zip = function.Parameters.Zip(Parameters, (parameter, expression) => (parameter, expression));
foreach (var (parameter, expression) in zip)
{
nestedScope[parameter] = new ParameterValue(expression.Execute(nestedScope));
}

var result = function.Call(nestedScope);

return result;
}

/// <inheritdoc />
public string ToString(IFormatter formatter)
=> formatter.Analyze(this);

/// <inheritdoc />
public override string ToString()
=> ToString(CommonFormatter.Instance);

/// <inheritdoc />
public TResult Analyze<TResult>(IAnalyzer<TResult> analyzer)
{
if (analyzer is null)
throw new ArgumentNullException(nameof(analyzer));

return analyzer.Analyze(this);
}

/// <inheritdoc />
public TResult Analyze<TResult, TContext>(
IAnalyzer<TResult, TContext> analyzer,
TContext context)
{
if (analyzer is null)
throw new ArgumentNullException(nameof(analyzer));

return analyzer.Analyze(this, context);
}

/// <summary>
/// Clones this instance of the <see cref="IExpression" />.
/// </summary>
/// <param name="function">The expression that returns function.</param>
/// <param name="parameters">The list of parameters of the function.</param>
/// <returns>
/// Returns the new instance of <see cref="IExpression" /> that is a clone of this instance.
/// </returns>
public IExpression Clone(IExpression? function = null, ImmutableArray<IExpression>? parameters = null)
=> new CallExpression(function ?? Function, parameters ?? Parameters);

/// <summary>
/// Gets the expression that returns the function.
/// </summary>
public IExpression Function { get; }

/// <summary>
/// Gets a list of parameters of the function.
/// </summary>
public ImmutableArray<IExpression> Parameters { get; }
}
Loading

0 comments on commit 25b8178

Please sign in to comment.