public IFunction CallInternal(IFunction[] arguments, string output, CompilationContext context)
        {
            if (arguments.Length < 2)
            {
                return(context.LogError(6, "memberwise requires at least 2 arguments"));
            }

            if (arguments.Any(a => a is AnyType))
            {
                return(AnyType.Instance);
            }

            if (arguments.Skip(1).Any(a => a.Inputs?.Length != 0))
            {
                return(context.LogError(14, "one or more memberwise arguments are functions"));
            }

            if (arguments.Skip(1).Any(a => a.Outputs == null))
            {
                return(context.LogError(14, "one or more memberwise arguments is non-introspectable"));
            }

            // TODO: Check all have same input signature?
            return(new MapFunction(arguments[0], arguments.Skip(1).ToArray()));
        }
Esempio n. 2
0
        /// <summary>
        /// Converts an Element Array instance (with count and index members) to
        /// a fixed-size list of Functions by evaluating each index.
        /// </summary>
        /// <returns>The evaluated array, or an empty array if there was an error</returns>
        private static IFunction[] EvaluateArray(IFunction function, CompilationContext context)
        {
            if (function == null)
            {
                throw new ArgumentNullException(nameof(function));
            }
            if (function.Inputs?.Length != 0)
            {
                context.LogError($"{function} needs to be an array, but it has inputs");
                return(Array.Empty <IFunction>());
            }

            var countExpr = function.Call("count", context).AsExpression(context);

            if (countExpr != null)
            {
                countExpr = ConstantFolding.Optimize(countExpr);
            }

            var count = (countExpr as Constant)?.Value;

            if (!count.HasValue)
            {
                context.LogError($"{function}'s count is not constant");
                return(Array.Empty <IFunction>());
            }

            var index = function.Call("index", context);

            return(Enumerable.Range(0, (int)count.Value)
                   .Select(i => index.Call(new[] { new Constant(i) }, context))
                   .ToArray());
        }
Esempio n. 3
0
        /// <summary>
        /// Calculates the serialized size of the given Function or Type
        /// </summary>
        /// <param name="value">The value or type in question</param>
        /// <param name="info">Where to log error messages</param>
        /// <returns>The size of the structure in singles, or null if there was a problem</returns>
        public static int?GetSize(this IFunction value, CompilationContext info)
        {
            if (value == Error.Instance)
            {
                return(null);
            }

            if (value is SerializableType)
            {
                return(0);
            }

            if (value.IsLeaf() || value.AsExpression(info) != null)
            {
                return(1);
            }

            if (value.Inputs?.Length != 0)
            {
                info.LogError($"Cannot serialize {value} because it has inputs");
                return(null);
            }

            if (value.Outputs == null)
            {
                info.LogError($"Cannot serialize {value} because it is not introspectable");
                return(null);
            }

            return(value.Outputs.Select(o => GetSize(value.Call(o.Name, info), info))
                   // NB: An ordinary 'Sum' will treat null as 0
                   .Aggregate((int?)0, (a, b) => (a == null || b == null) ? null : a + b));
        }
Esempio n. 4
0
        public override IFunction CallInternal(IFunction[] arguments, string output, CompilationContext context)
        {
            if (IsNamespace && arguments.Length == 0)
            {
                return(CompileIntermediate(arguments, output, context));
            }
            else if (IsNamespace && !IsClass)
            {
                return(context.LogError($"This is a Namespace, so it has no constructor"));
            }

            if (IsClass &&
                arguments.Length == Inputs.Length &&
                Outputs.All(p => p.Name != output) &&
                NamespaceMembers.Contains(output))
            {
                var memberFunction = CompileIntermediate(Array.Empty <IFunction>(), output, context);
                if (memberFunction.Inputs.Length > 0 && memberFunction.Inputs[0].Name == "this")
                {
                    var classInstance = this.Call(arguments, context);
                    return(classInstance.AsMethod(memberFunction, MakeCallSite(Ast), context));
                }
            }

            context.Push(MakeCallSite(Ast));
            if (this.CheckArguments(arguments, output, context) != null)
            {
                context.Pop();
                return(Error.Instance);
            }

            context.Pop();

            if (IsClass)
            {
                return(arguments[Array.FindIndex(Outputs, p => p.Name == output)]);
            }

            var outputPort = Outputs.First(p => p.Name == output);
            var outValue   = CompileIntermediate(arguments, output, context);
            var success    = outputPort.Type.SatisfiedBy(outValue, context);

            return(success switch
            {
                false => context.LogError("ELE0008", $"Output `{outputPort}` was not satisfied by its value `{outValue}` (See previous errors)"),
                null => Error.Instance,
                _ => outValue
            });
Esempio n. 5
0
        /// <summary>
        /// Compiles a single expression (e.g. part of an expression list)
        /// </summary>
        private IFunction CompileExpression(IFunction?previous, Match exprAst, CompilationStack stack, CompilationContext context)
        {
            switch (exprAst.Name)
            {
            case ElementAST.NumberExpression:
                return(new Constant((float)exprAst.Value));

            case ElementAST.VariableExpression:
                context.Push(MakeCallSite(exprAst));
                var variable = CompileFunction(exprAst.Text, stack, context);
                context.Pop();
                return(variable);

            case ElementAST.SubExpression:
                return(previous.Call(exprAst[ElementAST.SubExpressionName].Text, context, MakeCallSite(exprAst)));

            case ElementAST.CallExpression:
                // This is called a *lot*, so try to make it high performance:
                var args    = exprAst[ElementAST.CallArguments].Matches;
                var argList = new IFunction[args.Count];
                for (var i = 0; i < argList.Length; i++)
                {
                    argList[i] = CompileExpressionList(args[i], stack, context);
                }

                return(previous.Call(argList, context, MakeCallSite(exprAst)));

            default:
                return(context.LogError($"Unknown expression {exprAst}"));
            }
        }
Esempio n. 6
0
        public IFunction CallInternal(IFunction[] arguments, string name, CompilationContext context)
        {
            if (arguments.Length == 0)
            {
                return(context.LogError("ELE2000"));
            }

            switch (name)
            {
            case "count": return(new Constant(arguments.Length));

            case "index": return(new IndexFunction(arguments));

            default:
                return(context.LogError($"No output named {name}"));
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Adds a type to the global scope
        /// </summary>
        public void AddType(INamedType type, CompilationContext context)
        {
            if (FindType(type.Name, context) != null)
            {
                context.LogError("ELE0002", $"Duplicate type named {type.Name}");
            }

            _types.Add(type);
        }
Esempio n. 8
0
        /// <summary>
        /// Adds a function to the global scope
        /// </summary>
        public void AddFunction(INamedFunction function, CompilationContext context)
        {
            if (GetFunction(function.Name, context) != null)
            {
                context.LogError("ELE0002", $"Duplicate function named {function.Name}");
            }

            _functions.Add(function);
        }
Esempio n. 9
0
        /// <summary>
        /// Checks that the proposed arguments to a function are valid.
        /// </summary>
        /// <param name="function">The function about to be called</param>
        /// <param name="arguments">Ordered list of proposed inputs</param>
        /// <param name="output">The proposed output</param>
        /// <param name="info">The place to log messages on failure</param>
        /// <returns>Null on success, or Abort on failure (this allows using the null-coalesce operator)</returns>
        public static IFunction CheckArguments(this IFunction function, IFunction[] arguments, string output,
                                               CompilationContext info)
        {
            var success = true;
            var inputs  = function.Inputs;

            if (inputs != null)
            {
                if (arguments.Length != inputs.Length)
                {
                    info.LogError($"{function} Expected {inputs.Length} arguments but got {arguments.Length}");
                    success = false;
                }
                else
                {
                    for (var i = 0; i < inputs.Length; i++)
                    {
                        var arg = inputs[i].Type.SatisfiedBy(arguments[i], info);
                        if (arg == false)
                        {
                            info.LogError(
                                $"{function} Input {i} ({inputs[i]}) not satisfied by {arguments[i]} (see previous errors)");
                        }
                        if (arg != true)
                        {
                            success = false;
                        }
                    }
                }
            }

            if (output != null && function.Outputs != null && Array.FindIndex(function.Outputs, p => p.Name == output) < 0)
            {
                info.LogError($"Couldn't find output `{output}` in {function}");
                success = false;
            }

            if (arguments.Any(a => a is Error))
            {
                return(Error.Instance);
            }

            return(success ? null : Error.Instance);
        }
Esempio n. 10
0
            public DeserializedStructure(IFunction structure, Func <Expression> data, CompilationContext context)
            {
                if (structure.Inputs.Length > 0)
                {
                    context.LogError("ELE0001", $"Cannot deserialize {structure} because it has inputs");
                }

                Outputs       = structure.Outputs;
                _outputValues = Outputs.Select(o => structure.Call(o.Name, context).Deserialize(data, context)).ToArray();
            }
Esempio n. 11
0
        /// <summary>
        /// See the other overload of Serialize, but this time it takes a Queue rather than making a new array.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="dataOut">The queue to append new values to</param>
        /// <param name="context"></param>
        public static void Serialize(this IFunction value, Queue <Expression> dataOut, CompilationContext context)
        {
            var expr = value.AsExpression(context);

            if (expr != null)
            {
                dataOut.Enqueue(expr);
                return;
            }

            if (value.IsLeaf())
            {
                dataOut.Enqueue(Constant.Zero);
                return;
            }

            if (value is SerializableType)
            {
                return;
            }

            if (value.Inputs?.Length != 0)
            {
                context.LogError($"Cannot serialize {value} because it has inputs");
                return;
            }

            if (value.Outputs == null)
            {
                context.LogError($"{value} doesn't have known outputs");
                return;
            }

            // TODO: Arrays here?
            foreach (var output in value.Outputs)
            {
                Serialize(value.Call(output.Name, context), dataOut, context);
            }
        }
Esempio n. 12
0
		private PortInfo ParsePort(Match m, bool isReturn, CompilationContext info)
		{
			var ret = new PortInfo
			{
				Name = isReturn ? "return" : m[ElementAST.PortName].Text,
				Type = m[ElementAST.PortType] ? Parent.FindType(m[ElementAST.PortType].Text, info) : AnyType.Instance
			};

			if (ret.Type == null)
			{
				info.LogError(7, $"{this}: Type {m[ElementAST.PortType]} not found");
				ret.Type = AnyType.Instance;
			}

			return ret;
		}
Esempio n. 13
0
        public IFunction CompileFunction(string name, CompilationStack stack, CompilationContext context)
        {
            // First try to get a cached/inputted value...
            if (!stack.GetLocal(name, out var value))
            {
                // If that fails, look in the list of drivers:
                if (_drivers != null)
                {
                    if (_drivers.TryGetValue(name, out var statement))
                    {
                        if (statement[ElementAST.TypeStatement])                         // Check if this statement is a type declaration
                        {
                            var type = FindType(name, context);
                            value = new Constructor(type, context);
                        }
                        else
                        {
                            value = new CustomFunction(this, statement, stack, context, Source);
                        }

                        stack.Add(name, value);
                    }
                }
                // If there's no driver list (i.e. during an assignment), then the only output is 'return'
                // Since there's no other drivers we can assume
                else if (name == "return")
                {
                    value = CompileExpressionList(_astAssign, stack, context);
                    stack.Add(name, value);
                }
            }

            // Failing the above, try to find the value including parents in the stack
            // if we still cannot find a value, try using the captured compilation stack
            if (value == null && !stack.Get(name, out value) && _capturedCompilationStack != null)
            {
                value = Parent.CompileFunction(name, _capturedCompilationStack, context);
            }

            if (value == null)
            {
                return(context.LogError("ELE0007", name));
            }

            return(value.ResolveReturns(context, null));            // TODO: Keep variable information here?
        }
Esempio n. 14
0
        /// <summary>
        /// See the other overload of Deserialize, but this time it takes a delegate to generate new values
        /// rather than a fixed list.
        /// </summary>
        /// <returns>The new, deserialized structure mapped to the provided data.</returns>
        public static IFunction Deserialize(this IFunction function, Func <Expression> data, CompilationContext context)
        {
            if (function.IsLeaf())
            {
                return(data());
            }

            if (function is SerializableType)
            {
                return(function);
            }

            if (function?.Inputs == null)
            {
                context.LogError("ELE0001", $"Cannot deserialize `{function}` outputs because it's input ports have no defined type");
            }

            return(new DeserializedStructure(function, data, new CompilationContext()));
        }
Esempio n. 15
0
 IFunction IFunction.CallInternal(IFunction[] arguments, string output, CompilationContext context) =>
 context.LogError(9999, "Tried to call a leaf value");
Esempio n. 16
0
 IFunction IFunction.CallInternal(IFunction[] arguments, string output, CompilationContext context) => context.LogError("Can't call a number");
Esempio n. 17
0
        public bool?SatisfiedBy(IFunction value, CompilationContext info)
        {
            var inputs = Inputs.Select(p => p.Type).ToArray();
            // TODO: A better way of doing this!
            var outPorts = Outputs;

            // We are a function type
            if (inputs.Length > 0)
            {
                var called = this.Call(inputs, info);
                value = value.Call(inputs, info);

                // Calling this type returned another type
                if (called is IType type)
                {
                    return(type.SatisfiedBy(value, info));
                }

                // Calling the type returned something that isn't introspectable
                if (called.Outputs == null)
                {
                    // TODO: Check this. What about Abort?
                    info.LogError(14, $"Output of {value} is not introspectable");
                    return(false);
                }

                // Turns a structural thing into a bare list
                // Was required in past due to ambiguity between structural thing and multiple returns
                outPorts = called.Outputs.Select(o => new PortInfo {
                    Name = o.Name, Type = (IType)called.Call(o.Name, info)
                })
                           .ToArray();
            }

            // Now we check type satisfaction for each output port
            bool?success = true;

            foreach (var o in outPorts)
            {
                var val = value.Call(o.Name, info);
                if (val is CompilationError)
                {
                    success = null;
                }
                else
                {
                    var thisSuccess = o.Type.SatisfiedBy(val, info);
                    if (thisSuccess == false)
                    {
                        info.LogError(14, $"Output {o} not satisfied by {val}");
                        success = false;
                    }
                    else
                    {
                        success &= thisSuccess;
                    }
                }
            }

            return(success);
        }