示例#1
0
 /// <summary>
 /// Starting at the given node, gets a list of <see cref="Expression"/>s that include and follow this node's references.
 /// </summary>
 internal static IEnumerable<Expression> FlattenExpressions(VisualProgram context, StatementReference firstStatement) {
     var list = new List<Expression>();
     var currentNode = firstStatement.ResolveNode(context);
     while (currentNode is VisualStatement currentStatement) {
         list.Add(currentStatement.CreateExpression(context));
         currentNode = currentStatement.GetCompilerNextStatement(context)?.ResolveNode(context);
     }
     return list;
 }
 /// <summary>
 /// Creates an expression that sets the variable to the given expression value.
 /// </summary>
 /// <param name="context">The program context whose variable dictionary will be used.</param>
 /// <param name="variable">A reference to the variable.</param>
 /// <param name="value">The expression that represents the new value of the variable.</param>
 internal static Expression CreateSetterExpression(VisualProgram context, IVariableReference variable, Expression value)
 {
     variable.Validate(context);
     return(Call(
                context.Variables.compiledInstanceParameter,
                setVariableMethod,
                Constant(variable.Name, typeof(string)),
                Convert(value, typeof(object))
                ));
 }
示例#3
0
        public override Expression CreateExpression(VisualProgram context)
        {
            var branches = NodeUtils.FlattenExpressions(context, TrueBranch, FalseBranch);

            return(Expression.IfThenElse(
                       Condition.ResolveRequiredExpression(context),
                       Expression.Block(branches.branch1Flattened),
                       Expression.Block(branches.branch2Flattened)
                       ));
        }
示例#4
0
        public override Expression CreateExpression(VisualProgram context)
        {
            // If a start value is provided, set the target variable to that value. If not provided, do not
            var init = Start.HasValue
                                ? Variable.ResolveRequiredSetterExpression(context, Start.ResolveRequiredExpression(context))
                                : Expression.Empty();

            // Store some re-used expressions to prevent re-resolving them
            var variableExpr = Variable.ResolveRequiredGetterExpression(context);
            var changeExpr   = Change.ResolveExpression(context) ?? Expression.Constant(1d, typeof(double));
            var endExpr      = End.ResolveRequiredExpression(context);

            var loop = LoopFactory.CreateLoop(
                // Loop body
                Expression.Block(
                    // Run the given body
                    Body.ResolveStatement(context),

                    // Update the variable by 'Change' amount
                    Variable.ResolveRequiredSetterExpression(
                        context,
                        Expression.Add(variableExpr, changeExpr)
                        )
                    ),

                // Loop condition
                Expression.Condition(
                    // If the 'Change' value is >= 0...
                    Expression.GreaterThanOrEqual(
                        changeExpr,
                        Expression.Constant(0d, typeof(double))
                        ),
                    // Then the condition is whether the variable is less than or equal to the end
                    Expression.LessThanOrEqual(variableExpr, endExpr),
                    // Otherwise (if 'Change' is < 0) the condition is whether variable is greater than or equal to the end
                    Expression.GreaterThanOrEqual(variableExpr, endExpr)
                    )
                );

            // Return a block that sets the initial value then executes the loop
            return(Expression.Block(init, loop));
        }
        /// <summary>
        /// Compiles the entries in the given Program into delegates.
        /// Returns the delegates themselves and the delegate signatures (excluding the context parameter).
        /// </summary>
        private FunctionStore CompileEntryDelegates(VisualProgram program)
        {
            var progEntryNodes = program.Nodes.OfType <VisualEntry>().ToDictionary(ve => ve.VisualEntryId, ve => ve);

            return(program.Environment.EntryDefinitions.ToDictionary(
                       entry => entry.Key,
                       entry => {
                // If the program does not contain this entry node OR it exists but is not connected to any statements, then return no delegate
                if (!progEntryNodes.TryGetValue(entry.Value.Id, out var startNode) || !startNode.FirstStatement.HasValue)
                {
                    return null;
                }

                // Create a parameter for each expected/defined parameter
                var parameters = entry.Value.Parameters.Map(Expression.Parameter);

                // Compile and return the lambda
                return Expression.Lambda(
                    Expression.Block(
                        // Before doing the main body of the entry function, make expressions to copy the incoming parameters to variables
                        startNode.ParameterMap
                        .Where(mapping => !string.IsNullOrWhiteSpace(mapping.Value))                                         // Exclude any that don't actually map to anything
                        .Select(p => VariableAccessorFactory.CreateSetterExpression(
                                    program,
                                    VariableReference.Create(entry.Value.Parameters[p.Key], p.Value),
                                    parameters[p.Key]
                                    )
                                ).Concat(
                            // Then do the actual main body
                            NodeUtils.FlattenExpressions(program, startNode.FirstStatement)
                            )
                        ),

                    // All lambdas will get a context parameter
                    // We also pass the defined parameters to the lambda so it knows what types to expect and what signature to have.
                    // This needs to happen regardless or not of whether the parameters are actually used by the entry function as this determines the delegate's signature.
                    new[] { program.Variables.compiledInstanceParameter }.Concat(parameters)
                    ).Compile();
            },
                       StringComparer.OrdinalIgnoreCase
                       ));
        }
        internal CompiledProgramFactory(VisualProgram program)
        {
            this.program = program;
            functions    = CompileEntryDelegates(program);
            vars         = program.Variables.ToDictionary(v => v.Name, v => v.Clone(), StringComparer.OrdinalIgnoreCase);

            // Create a new type that extends/implements the TExtends type
            typeBuilder = moduleBuilder.DefineType("Dynamic_" + Guid.NewGuid().ToString("N"), TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout);
            if (typeof(TExtends).IsInterface)
            {
                typeBuilder.AddInterfaceImplementation(typeof(TExtends));
            }
            else
            {
                typeBuilder.SetParent(typeof(TExtends));
            }

            // Add the ICompiledInstanceBase (so long as the TExtends wasn't that)
            if (typeof(TExtends) != typeof(ICompiledInstanceBase))
            {
                typeBuilder.AddInterfaceImplementation(typeof(ICompiledInstanceBase));
            }

            // Generate the fields that will hold our compiled functions and variables
            functionsFieldBuilder = typeBuilder.DefineField(FunctionsFieldName, typeof(FunctionStore), FieldAttributes.Private | FieldAttributes.Static);
            varsFieldBuilder      = typeBuilder.DefineField(VariablesFieldName, typeof(VariableStore), FieldAttributes.Private);

            GenerateConstructors();
            GenerateDefaultMethods();
            GenerateDelegateBindings();
            GeneratePropertyBindings();

            // Create the type
            programType = typeBuilder.CreateType();

            // Set the value of the static functions field
            programType.GetField(FunctionsFieldName, BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, functions);
            varsFieldInfo = programType.GetField(VariablesFieldName, BindingFlags.NonPublic | BindingFlags.Instance);
        }
示例#7
0
        /// <summary>
        /// Finds the next node in common in the two branches. Can be used to help optimise the generation of branching flow statements
        /// such as if conditions, etc. Returns null if there is no common node.
        /// </summary>
        internal static StatementReference? FindNextSharedNode(VisualProgram context, StatementReference branch1, StatementReference branch2) {
            // Early return if either branch doesn't have any attached nodes - there cannot be a shared node if there are no nodes.
            if (!branch1.HasValue || !branch2.HasValue) return null;

            // Get a flat list of all Guids in the first branch
            var nodesIn1 = new List<Guid>();
            StatementReference? currentRef = branch1;
            while (currentRef.HasValue && currentRef.Value.HasValue) {
                nodesIn1.Add(currentRef?.nodeId!.Value);
                // Note that we use GetNextStatement here instead of NextStatement since, if it is a branch, we want the next shared statement of that branch
                currentRef = (currentRef?.ResolveNode(context) as VisualStatement)?.GetCompilerNextStatement(context);
            }

            // Search the second branch until we find the first occurance of the same node in the first branch
            currentRef = branch2;
            while (currentRef.HasValue && currentRef.Value.HasValue) {
                if (nodesIn1.Contains(currentRef.Value.nodeId!.Value))
                    return currentRef.Value;
                currentRef = (currentRef?.ResolveNode(context) as VisualStatement)?.GetCompilerNextStatement(context);
            }
            return null;
        }
 public MainWindowModel()
 {
     Program = new VisualProgram(env => env
                                 .ConfigureNodes(n => n
                                                 .IncludeDefault()
                                                 .Exclude <VisualProgrammer.Core.Nodes.Debug.Print>()
                                                 .Include <TraceStmt>()
                                                 )
                                 .ConfigureEntries(e => e
                                                   .Add("demoEntry", "Demo Entry")
                                                   .Add("demoEntry2", "Demo Entry 2")
                                                   ).ConfigureLockedVariables(v => v
                                                                              .Add <int>("someIntVar", 20)
                                                                              .Add <double>("somenewLockedDouble")
                                                                              ),
                                 new VisualNodeCollection(),
                                 new VariableCollection {
         { "someStringVar", typeof(string), "Hello from the someStringVar!" },
         { "otherStrVar", typeof(string), "Greetings from the otherStrVar!" },
         { "someIntVar", typeof(int), 10 }
     }
                                 );
 }
示例#9
0
 public override Expression CreateExpression(VisualProgram context) => opMap[SelectedOperation](
     LHS.ResolveRequiredExpression(context),
     RHS.ResolveRequiredExpression(context)
     );
示例#10
0
 public override Expression CreateExpression(VisualProgram context) => Expression.Condition(
     Condition.ResolveRequiredExpression(context),
     A.ResolveRequiredExpression(context),
     B.ResolveRequiredExpression(context)
     );
示例#11
0
 public override Expression CreateExpression(VisualProgram context) =>
 Expression.Constant(Value, typeof(TValue));
 public override Expression CreateExpression(VisualProgram context) => Variable.ResolveRequiredSetterExpression(context, opMap[SelectedOperation](
                                                                                                                    Variable.ResolveRequiredGetterExpression(context),
                                                                                                                    Value.ResolveRequiredExpression(context)
                                                                                                                    )
                                                                                                                );
示例#13
0
 public override Expression CreateExpression(VisualProgram context)
 => Expression.Call(null, write, PrintValue.ResolveRequiredExpression(context));
示例#14
0
 public override Expression CreateExpression(VisualProgram context) =>
 Variable.ResolveRequiredSetterExpression(context, Value.ResolveRequiredExpression(context));
示例#15
0
 public override Expression CreateExpression(VisualProgram context) => Expression.Call(
     concat,
     First.ResolveExpression(context) ?? Expression.Constant(""),
     Second.ResolveExpression(context) ?? Expression.Constant("")
     );
 /// <summary>
 /// During compilation, this method will be used to get the next statement after this once.
 /// For most circumstances, the default implementation is fine, but for branching statements this will likely return
 /// the first shared node of the branches.
 /// </summary>
 public virtual StatementReference?GetCompilerNextStatement(VisualProgram context) => NextStatement;
示例#17
0
 public override Expression CreateExpression(VisualProgram context) => Expression.Call(
     Value.ResolveRequiredExpression(context),
     typeof(TSource).GetMethod("ToString", Array.Empty <Type>())
     );
示例#18
0
 public override Expression CreateExpression(VisualProgram context) => LoopFactory.CreateLoop(
     Body.ResolveStatement(context),
     Condition.ResolveRequiredExpression(context)
     );
示例#19
0
        /// <summary>
        /// Starting at the given branch entry points, gets a list of <see cref="Expression"/>s that include and follow the branch, UNTIL a shared
        /// node is found. The shared node will not be included in the returned lists.
        /// </summary>
        internal static (IEnumerable<Expression> branch1Flattened, IEnumerable<Expression> branch2Flattened) FlattenExpressions(VisualProgram context, StatementReference branch1, StatementReference branch2) {
            var firstShared = FindNextSharedNode(context, branch1, branch2);

            // Early return if there is no shared node
            if (firstShared == null) return (FlattenExpressions(context, branch1), FlattenExpressions(context, branch2));

            List<Expression> flatten(StatementReference start) {
                var list = new List<Expression>();
                StatementReference? curRef = start;
                while (curRef.HasValue && curRef.Value.HasValue && curRef != firstShared && curRef.Value.ResolveNode(context) is VisualStatement statement) {
                    list.Add(statement.CreateExpression(context));
                    curRef = statement.GetCompilerNextStatement(context);
                }
                return list;
            }
            return (flatten(branch1), flatten(branch2));
        }