private Expression PredefinedAtom_SetContains(ParseTreeNode root, CompilerState state)
        {
            root.RequireChildren(2);

            var arg1Node = root.RequireChild(null, 1, 0, 0);
            var hashset  = state.ParentRuntime.Analyze(arg1Node, state);

            hashset.RequireNonVoid(arg1Node);
            if (hashset.Type.IsValueType)
            {
                throw new CompilationException("Set must be of reference type", arg1Node);
            }

            if (hashset is ConstantExpression)
            {
                throw new CompilationException("Set must not be a constant expression", arg1Node);
            }

            var arg2Node = root.RequireChild(null, 1, 0, 1);
            var element  = state.ParentRuntime.Analyze(arg2Node, state);

            var methods = hashset.Type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy);

            foreach (var method in methods)
            {
                if (method.Name.Equals("Contains"))
                {
                    var methodArgs = method.GetParameters();
                    if (methodArgs.Length == 1 && methodArgs[0].ParameterType != typeof(object))
                    {
                        Expression adjusted;
                        if (ExpressionTreeExtensions.TryAdjustReturnType(arg2Node, element, methodArgs[0].ParameterType, out adjusted))
                        {
                            return(Expression.Condition(
                                       Expression.ReferenceEqual(hashset, Expression.Constant(null)),
                                       Expression.Constant(false, typeof(bool)),
                                       Expression.Call(hashset, method, adjusted)));
                        }
                    }
                }
            }

            throw new CompilationException(
                      "Could not find a public instance method 'Contains' to match element type " + element.Type.FullName, arg1Node);
        }
        private static Expression PredefinedAtom_Cast(ParseTreeNode root, CompilerState state)
        {
            root.RequireChildren(2);

            var arg1Node = root.RequireChild(null, 1, 0, 0);
            var value    = state.ParentRuntime.Analyze(arg1Node, state).RemoveNullability();

            var arg2Node   = root.RequireChild("string", 1, 0, 1);
            var targetType = RequireSystemType(arg2Node);

            if (value.IsVoid())
            {
                return(ExpressionTreeExtensions.GetDefaultExpression(targetType));
            }

            // bluntly attempt to convert the type; will throw if types are not compatible
            return(ConstantHelper.TryEvalConst(root, value, ExpressionType.Convert, targetType));
        }
        private static Expression PredefinedAtom_IsDefault(ParseTreeNode root, CompilerState state)
        {
            var funArgs = ExpressionTreeExtensions.UnwindTupleExprList(root.RequireChild("exprList", 1, 0));

            funArgs.RequireChildren(1);

            var arg1Node = funArgs.ChildNodes[0];
            var value    = state.ParentRuntime.Analyze(arg1Node, state).RemoveNullability();

            if (value.IsString())
            {
                if (value is ConstantExpression)
                {
                    return(Expression.Constant(string.IsNullOrEmpty((string)((ConstantExpression)value).Value), typeof(Boolean)));
                }

                return(Expression.Call(ReflectionHelper.StringIsNullOrEmpty, value));
            }

            return(ConstantHelper.TryEvalConst(root, value, Expression.Default(value.Type), ExpressionType.Equal));
        }
        /// <summary>
        /// Produces .NET Expression object from the given abstract syntax tree.
        /// Supports re-entrancy, useful for expression generators (see <see cref="AtomMetadata.ExpressionGenerator"/>).
        /// </summary>
        /// <param name="root">Parse tree root</param>
        /// <param name="state">Compiler state</param>
        /// <returns>Expression node with verified logical data type</returns>
        /// <exception cref="ArgumentNullException"><paramref name="root"/> is null</exception>
        /// <exception cref="ArgumentNullException"><paramref name="state"/> is null</exception>
        /// <exception cref="CompilationException">Compilation errors, check exception details</exception>
        public Expression Analyze(ParseTreeNode root, CompilerState state)
        {
            switch (root.Term.Name)
            {
            case "exprList":
            case "tuple":
                root.RequireChildren(1);
                var newRoot = ExpressionTreeExtensions.UnwindTupleExprList(root.ChildNodes[0]);
                return(Analyze(newRoot, state));

            case "betweenExpr":
                return(BuildBetweenExpression(root, state));

            case "binExpr":
                return(BuildBinaryExpression(root, state));

            case "unExpr":
                return(BuildUnaryExpression(root, state));

            case "case":
                return(BuildCaseStatementExpression(root, state));

            case "Id":
                return(BuildIdentifierExpression(root, state));

            case "number":
                return(BuildNumericConstantExpression(root, state));

            case "string":
                return(BuildStringLiteralExpression(root, state));

            case "funCall":
                return(BuildFunCallExpression(root, state));

            default:
                throw new CompilationException("Term not yet supported: " + root.Term.Name, root);
            }
        }
 private static Expression PredefinedAtom_Null(ParseTreeNode root, CompilerState state)
 {
     // clients are responsible for translating this expression into appropriate type
     return(ExpressionTreeExtensions.MakeNewNullable(typeof(UnboxableNullable <ExpressionTreeExtensions.VoidTypeMarker>)));
 }
Exemplo n.º 6
0
        private Expression BuildInclusionExpression(ParseTreeNode root, Expression leftExpr, string op, CompilerState state)
        {
            root.RequireChildren(3);

            var rightNodeList = ExpressionTreeExtensions.UnwindTupleExprList(root.ChildNodes[2]);

            if (rightNodeList.Term.Name == "Id")
            {
                throw new CompilationException("Parameterized IN statement is not yet supported, consider using function SetContains", rightNodeList);
            }

            if (rightNodeList.Term.Name != "exprList")
            {
                throw new CompilationException("Argument for IN operator must be a list of expressions", root);
            }

            // Expression text is not supposed to be used to pass tens and hundreds of thousands of IDs in plain text.
            // Use parameters for large argument sets.
            rightNodeList.RequireChildren(1, 1000);

            leftExpr = leftExpr.RemoveNullability();

            // compile a method to enumerate values in the argument set
            var valueEnumerator = ReflectionHelper.EnumerateValues.MakeGenericMethod(leftExpr.Type);
            // invoke enumerator, output is a hashset
            object matchingSet;

            try
            {
                matchingSet = valueEnumerator.Invoke(null, new object[] { this, rightNodeList, state });
            }
            catch (TargetInvocationException e)
            {
                if (e.InnerException == null)
                {
                    throw;
                }
                throw e.InnerException;
            }

            // how many items do we have there?
            var countProperty = matchingSet.GetType().GetProperty("Count", BindingFlags.Instance | BindingFlags.Public);
            var count         = (int)(countProperty.GetValue(matchingSet));

            Expression contains;
            var        leftArgConst = leftExpr as ConstantExpression;

            if (leftArgConst != null)
            {
                // since list is constant and argument is constant, let's just evaluate it
                var setContainsMethod = ReflectionHelper.GetOrAddMethod1(matchingSet.GetType(), "Contains", leftExpr.Type);

                try
                {
                    contains = Expression.Constant(setContainsMethod.Invoke(matchingSet, new[] { leftArgConst.Value }), typeof(bool));
                }
                catch (TargetInvocationException e)
                {
                    if (e.InnerException == null)
                    {
                        throw;
                    }
                    throw e.InnerException;
                }
            }
            else
            {
                var threshold = leftExpr.IsInteger() ? 15 : 5;

                // for small sets of values, just create a chain of IF/THEN/ELSE statements
                if (count <= threshold)
                {
                    var         isString         = leftExpr.IsString();
                    var         enumeratorMethod = ReflectionHelper.GetOrAddMethod0(matchingSet.GetType(), "GetEnumerator");
                    IEnumerator enumerator;
                    try
                    {
                        enumerator = (IEnumerator)enumeratorMethod.Invoke(matchingSet, null);
                    }
                    catch (TargetInvocationException e)
                    {
                        if (e.InnerException == null)
                        {
                            throw;
                        }
                        throw e.InnerException;
                    }

                    contains = null;
                    while (enumerator.MoveNext())
                    {
                        var next = isString
                            ? PrepareStringEquality(rightNodeList, leftExpr, Expression.Constant(enumerator.Current, leftExpr.Type))
                            : Expression.Equal(leftExpr, Expression.Constant(enumerator.Current, leftExpr.Type));
                        contains = contains == null ? next : Expression.OrElse(contains, next);
                    }
                }
                else
                {
                    // for larger sets, wire our matchingSet into this expression as constant reference
                    // it will be kept alive by garbage collector, and will be collected when expression is collected
                    var setContainsMethod = ReflectionHelper.GetOrAddMethod1(matchingSet.GetType(), "Contains", leftExpr.Type);
                    contains = Expression.Call(Expression.Constant(matchingSet), setContainsMethod, leftExpr);
                }
            }

            if (op.StartsWith("not "))
            {
                contains = ConstantHelper.TryEvalConst(root, contains, ExpressionType.Not, typeof(bool));
            }

            return(contains);
        }
Exemplo n.º 7
0
        private Expression BuildBinaryExpression(ParseTreeNode root, CompilerState state)
        {
            root.RequireChildren(3);

            var leftNode = root.ChildNodes[0];
            var leftExpr = Analyze(leftNode, state);

            var op = GetBinaryOperator(root.ChildNodes[1]);

            if (op == "in" || op == "not in")
            {
                return(BuildInclusionExpression(root, leftExpr, op, state));
            }

            var rightNode = root.ChildNodes[2];
            var rightExpr = Analyze(rightNode, state);

            if (!ExpressionTreeExtensions.TryAdjustVoid(ref leftExpr, ref rightExpr))
            {
                throw new CompilationException("This operation is not defined when both arguments are void", root);
            }

            Expression expr;

            leftExpr  = leftExpr.RemoveNullability();
            rightExpr = rightExpr.RemoveNullability();

            if (leftExpr.IsDateTime() && rightExpr.IsDateTime() ||
                leftExpr.IsTimeSpan() && rightExpr.IsTimeSpan())
            {
                #region DateTime and DateTime, or TimeSpan and TimeSpan
                switch (op)
                {
                case "+":
                    if (leftExpr.IsDateTime() && rightExpr.IsDateTime())
                    {
                        throw new CompilationException("Datetime values cannot be added to one another", root);
                    }
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Add);
                    break;

                case "-":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Subtract);
                    break;

                case "=":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Equal);
                    break;

                case "!=":
                case "<>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.NotEqual);
                    break;

                case ">":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.GreaterThan);
                    break;

                case "<":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.LessThan);
                    break;

                case "<=":
                case "!>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.LessThanOrEqual);
                    break;

                case ">=":
                case "!<":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.GreaterThanOrEqual);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported for datetime values: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsDateTime() && rightExpr.IsTimeSpan())
            {
                #region DateTime and TimeSpan or TimeSpan and DateTime
                switch (op)
                {
                case "+":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Add);
                    break;

                case "-":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Subtract);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported for datetime and timespan: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsTimeSpan() && rightExpr.IsDateTime())
            {
                #region TimeSpan and DateTime
                switch (op)
                {
                case "+":
                    expr = ConstantHelper.TryEvalConst(root, rightExpr, leftExpr, ExpressionType.Add);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported for timespan and datetime: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsString() && rightExpr.IsString())
            {
                #region String and String
                switch (op)
                {
                case "+":
                    var concat = ReflectionHelper.StringConcat;
                    expr = ConstantHelper.TryEvalConst(root, concat, leftExpr, rightExpr);
                    break;

                case "=":
                case "!=":
                case "<>":
                    expr = PrepareStringEquality(root, leftExpr, rightExpr);
                    if (op[0] != '=')
                    {
                        expr = ConstantHelper.TryEvalConst(root, expr, ExpressionType.Not, expr.Type);
                    }
                    break;

                case ">":
                    expr = ConstantHelper.TryEvalConst(root, PrepareStringComparison(root, leftExpr, rightExpr), Expression.Constant(0), ExpressionType.GreaterThan);
                    break;

                case "<":
                    expr = ConstantHelper.TryEvalConst(root, PrepareStringComparison(root, leftExpr, rightExpr), Expression.Constant(0), ExpressionType.LessThan);
                    break;

                case "<=":
                case "!>":
                    expr = ConstantHelper.TryEvalConst(root, PrepareStringComparison(root, leftExpr, rightExpr), Expression.Constant(0), ExpressionType.LessThanOrEqual);
                    break;

                case ">=":
                case "!<":
                    expr = ConstantHelper.TryEvalConst(root, PrepareStringComparison(root, leftExpr, rightExpr), Expression.Constant(0), ExpressionType.GreaterThanOrEqual);
                    break;

                case "like":
                    throw new CompilationException("Instead of LIKE, use predefined functions StartsWith, EndsWith and Contains", root.ChildNodes[1]);

                default:
                    throw new CompilationException("Binary operator not supported for strings: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsNumeric() && rightExpr.IsNumeric())
            {
                #region Numeric and Numeric
                ExpressionTreeExtensions.AdjustArgumentsForBinaryOperation(ref leftExpr, ref rightExpr, root);

                switch (op)
                {
                case "+":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Add);
                    break;

                case "-":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Subtract);
                    break;

                case "*":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Multiply);
                    break;

                case "/":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Divide);
                    break;

                case "%":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Modulo);
                    break;

                case "&":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.And);
                    break;

                case "|":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Or);
                    break;

                case "^":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.ExclusiveOr);
                    break;

                case "<":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.LessThan);
                    break;

                case "<=":
                case "!>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.LessThanOrEqual);
                    break;

                case ">":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.GreaterThan);
                    break;

                case ">=":
                case "!<":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.GreaterThanOrEqual);
                    break;

                case "=":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Equal);
                    break;

                case "!=":
                case "<>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.NotEqual);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported yet for numerics: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsBoolean() && rightExpr.IsBoolean())
            {
                #region Boolean and Boolean
                switch (op)
                {
                case "and":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.AndAlso);
                    break;

                case "or":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.OrElse);
                    break;

                case "xor":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.ExclusiveOr);
                    break;

                case "=":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Equal);
                    break;

                case "!=":
                case "<>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.NotEqual);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported yet for booleans: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else
            {
                throw new CompilationException(string.Format("Binary operator {0} is not yet supported for types {1} and {2}",
                                                             op, leftExpr.Type.FullName, rightExpr.Type.FullName), root.ChildNodes[1]);
            }

            return(expr);
        }
Exemplo n.º 8
0
        private Expression BuildCaseStatementExpression(CompilerState state, ParseTreeNode whenThenListNode, Expression caseDefault)
        {
            // now start building on top of the tail, right to left,
            // also making sure that types are compatible
            var tail = caseDefault;

            for (var i = whenThenListNode.ChildNodes.Count - 1; i >= 0; i--)
            {
                var caseWhenThenNode = whenThenListNode.RequireChild("caseWhenThen", i);
                caseWhenThenNode.RequireChildren(4);
                var whenNode = caseWhenThenNode.RequireChild(null, 1);
                var thenNode = caseWhenThenNode.RequireChild(null, 3);

                if (whenNode.Term.Name == "tuple")
                {
                    throw new CompilationException("When variable for CASE is not specified, you can only have one expression in each WHEN clause", whenNode);
                }

                // in this flavor of CASE, we are building a sequence of IIFs
                // it requires that our "WHEN" clause is of non-nullable boolean type
                var when = Analyze(whenNode, state).RemoveNullability();
                when.RequireBoolean(whenNode);

                var then = Analyze(thenNode, state);

                // try to auto-adjust types of this "THEN" and current tail expression if needed
                if (tail != null)
                {
                    Expression adjusted;
                    if (ExpressionTreeExtensions.TryAdjustReturnType(thenNode, then, tail.Type, out adjusted))
                    {
                        then = adjusted;
                    }
                    else if (ExpressionTreeExtensions.TryAdjustReturnType(thenNode, tail, then.Type, out adjusted))
                    {
                        tail = adjusted;
                    }
                    else
                    {
                        throw new CompilationException(
                                  string.Format(
                                      "Incompatible types within CASE statement. Tail is of type {0}, and then is of type {1}",
                                      tail.Type.FullName, then.Type.FullName), thenNode);
                    }
                }

                if (when is ConstantExpression)
                {
                    if ((bool)((ConstantExpression)when).Value)
                    {
                        tail = then;
                    }
                }
                else
                {
                    tail = Expression.Condition(when, then, tail ?? ExpressionTreeExtensions.GetDefaultExpression(then.Type));
                }
            }

            return(tail);
        }
Exemplo n.º 9
0
        private Expression BuildSwitchStatementExpression(CompilerState state, ParseTreeNode caseVariableNode, ParseTreeNode whenThenListNode, Expression caseDefault)
        {
            var switchVariable = Analyze(caseVariableNode.ChildNodes[0], state);

            switchVariable.RequireNonVoid(caseVariableNode.ChildNodes[0]);

            if (switchVariable is ConstantExpression)
            {
                throw new CompilationException("CASE variable should not be a constant value", caseVariableNode);
            }

            var        cases              = new List <Tuple <Expression[], Expression, ParseTreeNode> >(whenThenListNode.ChildNodes.Count);
            Expression firstNonVoidThen   = null;
            var        mustReturnNullable = false;

            foreach (var caseWhenThenNode in whenThenListNode.ChildNodes)
            {
                caseWhenThenNode.RequireChildren(4);
                var whenNodesRoot = ExpressionTreeExtensions.UnwindTupleExprList(caseWhenThenNode.RequireChild(null, 1));
                var thenNode      = caseWhenThenNode.RequireChild(null, 3);

                IList <ParseTreeNode> whenNodes;
                if (whenNodesRoot.Term.Name == "exprList")
                {
                    whenNodes = whenNodesRoot.ChildNodes;
                }
                else
                {
                    whenNodes = new[] { whenNodesRoot };
                }

                var when = new Expression[whenNodes.Count];
                for (var i = 0; i < whenNodes.Count; i++)
                {
                    var whenNode = whenNodes[i];
                    when[i] = Analyze(whenNode, state);

                    if (!when[i].IsVoid() && !(when[i] is ConstantExpression))
                    {
                        throw new CompilationException("CASE statement with a test variable requires WHEN clauses to be constant values", whenNode);
                    }

                    Expression adjusted;
                    if (ExpressionTreeExtensions.TryAdjustReturnType(whenNode, when[i], switchVariable.Type, out adjusted))
                    {
                        when[i] = adjusted;
                    }
                    else
                    {
                        throw new CompilationException(
                                  string.Format(
                                      "Could not adjust WHEN value type {0} to CASE argument type {1}",
                                      when[i].Type.FullName, switchVariable.Type.FullName), whenNode);
                    }
                }

                var then = Analyze(thenNode, state);
                cases.Add(new Tuple <Expression[], Expression, ParseTreeNode>(when, then, thenNode));

                if (then.IsVoid())
                {
                    // if there is at least one "void" return value, resulting value must be nullable
                    mustReturnNullable = true;
                }
                else if (firstNonVoidThen == null)
                {
                    firstNonVoidThen = then;
                }
            }

            if (firstNonVoidThen == null && !caseDefault.IsVoid())
            {
                firstNonVoidThen = caseDefault;
            }

            var adjustedCaseDefault = caseDefault;

            // now try to adjust whatever remaining VOID "then-s" to the first-met non-void then
            // if all THENs are void, then just leave it as-is - type will be adjusted by caller
            if (firstNonVoidThen != null)
            {
                if (mustReturnNullable && firstNonVoidThen.Type.IsValueType && !firstNonVoidThen.IsNullableType())
                {
                    firstNonVoidThen = ExpressionTreeExtensions.MakeNewNullable(
                        typeof(UnboxableNullable <>).MakeGenericType(firstNonVoidThen.Type),
                        firstNonVoidThen);
                }

                for (var i = 0; i < cases.Count; i++)
                {
                    var thenNode = cases[i].Item3;
                    var then     = cases[i].Item2;

                    if (!ReferenceEquals(then, firstNonVoidThen) && then.IsVoid())
                    {
                        Expression adjusted;
                        if (ExpressionTreeExtensions.TryAdjustReturnType(thenNode, then, firstNonVoidThen.Type, out adjusted))
                        {
                            cases[i] = new Tuple <Expression[], Expression, ParseTreeNode>(cases[i].Item1, adjusted, cases[i].Item3);
                        }
                        else
                        {
                            throw new CompilationException(
                                      string.Format(
                                          "Could not adjust THEN value type {0} to first-met THEN value type {1}",
                                          then.Type.FullName, firstNonVoidThen.Type.FullName), thenNode);
                        }
                    }
                }

                if (caseDefault != null &&
                    !ExpressionTreeExtensions.TryAdjustReturnType(caseVariableNode, caseDefault, firstNonVoidThen.Type, out adjustedCaseDefault))
                {
                    throw new CompilationException(
                              string.Format(
                                  "Could not adjust CASE default value's type {0} to first-met THEN value type {1}",
                                  caseDefault.Type.FullName, firstNonVoidThen.Type.FullName), caseVariableNode);
                }
            }

            if (adjustedCaseDefault == null)
            {
                adjustedCaseDefault = ExpressionTreeExtensions.GetDefaultExpression(
                    firstNonVoidThen == null
                        ? typeof(UnboxableNullable <ExpressionTreeExtensions.VoidTypeMarker>)
                        : firstNonVoidThen.Type);
            }

            return(Expression.Switch(
                       switchVariable, adjustedCaseDefault, null,
                       cases.Select(x => Expression.SwitchCase(x.Item2, x.Item1))));
        }
Exemplo n.º 10
0
        private Expression BuildIdentifierRootExpression(ParseTreeNode root, CompilerState state)
        {
            AtomMetadata atom;
            var          name = root.ChildNodes[0].Token.ValueString;

            // first, look for an argument with this name
            var argument = state.TryGetArgumentByName(name);

            if (argument != null)
            {
                return(argument);
            }

            var context = state.Context;

            // next, see if we have a field or property on the context (if any context present)
            var contextBoundExpression = TryGetFieldOrPropertyInfoFromContext(name, context);

            if (contextBoundExpression != null)
            {
                return(contextBoundExpression);
            }

            // and only then look through available IDENTIFIER atoms
            if (m_atoms.TryGetValue(name, out atom) && atom.AtomType == AtomType.Identifier)
            {
                if (atom.ExpressionGenerator != null)
                {
                    return(atom.ExpressionGenerator(root, state));
                }

                if (atom.MethodInfo == null)
                {
                    // internal error, somebody screwed up with configuration of runtime
                    throw new Exception("ExpressionGenerator and MethodInfo are both null on atom: " + atom.Name);
                }

                // no arguments? great
                var paramInfo = atom.MethodInfo.GetParameters();
                if (paramInfo.Length == 0)
                {
                    return(BuildFunctorInvokeExpression(atom, (Expression[])null));
                }

                // any arguments? must have exactly one argument, context must be registered, and context type must be adjustable to this method's arg type
                if (context == null)
                {
                    throw new CompilationException("Atom's MethodInfo cannot be used for an Id expression, because context is not available: " + atom.Name, root);
                }

                Expression adjustedContext;
                if (paramInfo.Length > 1 || !ExpressionTreeExtensions.TryAdjustReturnType(root, context, paramInfo[0].ParameterType, out adjustedContext))
                {
                    throw new CompilationException("Atom's MethodInfo may only have either zero arguments or one argument of the same type as expression context: " + atom.Name, root);
                }

                return(BuildFunctorInvokeExpression(atom, adjustedContext));
            }

            // still nothing found? ask IDENTIFIER atom handlers
            foreach (var handler in m_atomHandlers)
            {
                if (handler.AtomType != AtomType.Identifier)
                {
                    continue;
                }

                if (handler.ExpressionGenerator == null)
                {
                    // internal error, somebody screwed up with configuration of runtime
                    throw new Exception("ExpressionGenerator is null on atom handler: " + handler.Name);
                }

                // only pass the first portion of dot-notation identifier to handler
                var result = handler.ExpressionGenerator(root.ChildNodes[0], state);
                if (result != null)
                {
                    return(result);
                }
            }

            throw new CompilationException("Unknown atom: " + name, root);
        }