Ejemplo n.º 1
0
        /// <summary>
        /// Converts the given abstract expression into a list of possible AST expressions.
        /// Throws an exception if the translation was not possible because of unfinished symbols
        /// </summary>
        /// <param name="unit"></param>
        /// <param name="scope"></param>
        /// <param name="value"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private IReadOnlyList <Expression> ConvertExpression(TranslationUnit unit, IScope scope, Grammar.Expression value, ExpressionContext context = null)
        {
            if (value is NumberLiteral num)
            {
                var literals = new List <Expression>();

                // try hex first, then decimal
                if (ulong.TryParse(num.Value.Substring(2), NumberStyles.HexNumber, SystemFormat, out ulong u))
                {
                    if (u <= 0xFF)
                    {
                        literals.Add(new ByteLiteral((byte)u));
                    }
                    literals.Add(new UIntLiteral(u));
                }
                else if (ulong.TryParse(num.Value, NumberStyles.Integer, SystemFormat, out u))
                {
                    if (u <= 0xFF)
                    {
                        literals.Add(new ByteLiteral((byte)u));
                    }
                    literals.Add(new UIntLiteral(u));
                }

                // try hex first, then decimal
                if (long.TryParse(num.Value.Substring(2), NumberStyles.HexNumber, SystemFormat, out long i))
                {
                    literals.Add(new IntLiteral(i));
                }
                else if (long.TryParse(num.Value, NumberStyles.Integer, SystemFormat, out i))
                {
                    literals.Add(new IntLiteral(i));
                }

                // also try double separated
                if (double.TryParse(num.Value, NumberStyles.Number, SystemFormat, out double dbl))
                {
                    literals.Add(new RealLiteral(dbl));
                }

                return(literals.ToArray());
            }
            else if (value is Grammar.StringLiteral str)
            {
                // this could be translated into an ArrayLiteral,
                // but this allows a certain degree of optimization
                return(new[] { new StringLiteral(str.Text) });
            }
            else if (value is CharacterLiteral chr)
            {
                return(new[] { new CharLiteral(chr.Codepoint) });
            }
            else if (value is ArrayLiteral array)
            {
                var expr = new ArrayExpression();
                expr.Items = new Expression[array.Values.Count];
                for (int i = 0; i < expr.Items.Length; i++)
                {
                    int idx  = i;
                    var elem = ConvertExpression(unit, scope, array.Values[idx]);
                    if (elem == null)
                    {
                        Error.InvalidExpression(value);
                        return(null);
                    }
                    expr.Items[idx] = elem.Single();
                }

                if (expr.Items.Any(i => i is null))
                {
                    throw Error.Critical("Not all array elements could be translated!");
                }

                var type = FindCommonType(expr.Items.Select(i => i.Type));
                if (type == null)
                {
                    throw Error.NoCommmonType();
                }

                expr.ItemType = type;

                return(new[] { expr });
            }
            else if (value is Grammar.FunctionLiteral function)
            {
                var type = ConvertType(unit, scope, function.Type) as FunctionType;
                if (type == null)
                {
                    throw Error.UnresolvableType(function.Type);
                }

                var fun = new UserFunction(type);

                var paramscope = new SimpleScope();
                for (int i = 0; i < type.Parameters.Length; i++)
                {
                    var param = type.Parameters[i];
                    paramscope.Add(new Symbol(param.Type, param.Name)
                    {
                        Kind           = SymbolKind.Parameter,
                        ParameterIndex = i,
                    });
                }

                fun.Scope.Push(scope);
                fun.Scope.Push(paramscope);

                var ctx = new BlockContext
                {
                    EnclosingType = type
                };

                // IMPORTANT:
                // Use task system here as a function literal body is independent from its type
                // and can be translated completly separated from the rest of the expression
                unit.AddTask(() =>
                {
                    var body = ConvertStatement(unit, fun.Scope, function.Body, ctx);
                    if (body == null)
                    {
                        return(Error.UntranslatableStatement(function.Body));
                    }
                    fun.Body = body;
                    return(Error.None);
                });

                unit.Module.Functions.Add(fun);

                return(new[] { new FunctionLiteral(fun) });
            }
            else if (value is VariableReference vref)
            {
                var syms = scope.Where(s => s.Name.ID == vref.Variable).ToArray();
                if (syms.Length == 0)
                {
                    throw Error.SymbolNotFound(vref.Variable);
                }
                return(syms.Select(s => new SymbolReference(s)).ToArray());
            }
            else if (value is UnaryOperation unop)
            {
                var val = ConvertExpression(unit, scope, unop.Operand);
                if (val == null)
                {
                    throw Error.InvalidExpression(unop.Operand);
                }

                var results = new List <FunctionCall>();
                foreach (var item in val)
                {
                    var type = FunctionType.CreateUnaryOperator(Type.UnknownType, item.Type);

                    var sig = new Signature(type, unop.Operator);
                    if (scope.HasSymbol(sig) == false)
                    {
                        continue;
                    }

                    results.Add(new FunctionCall(new SymbolReference(scope[sig]), new[] { item }));
                }
                if (results.Count == 0)
                {
                    throw Error.UnknownOperator(unop);
                }
                return(results);
            }
            else if (value is BinaryOperation binop)
            {
                var lhslist = ConvertExpression(unit, scope, binop.LeftHandSide);
                var rhslist = ConvertExpression(unit, scope, binop.RightHandSide);

                var results = new List <FunctionCall>();

                foreach (var lhs in lhslist)
                {
                    foreach (var rhs in rhslist)
                    {
                        var type = FunctionType.CreateBinaryOperator(
                            returnType: Type.UnknownType,
                            leftType: lhs.Type,
                            rightType: rhs.Type);

                        var sig = new Signature(type, binop.Operator);

                        if (scope.HasSymbol(sig) == false)
                        {
                            continue;
                        }

                        results.Add(new FunctionCall(new SymbolReference(scope[sig]), new[] { lhs, rhs }));
                    }
                }
                if (results.Count == 0)
                {
                    Error.UnknownOperator(binop);
                }
                return(results);
            }
            else if (value is FunctionCallExpression fncall)
            {
                var results   = new List <FunctionCall>();
                var functions = ConvertExpression(unit, scope, fncall.Value);
                foreach (var func in functions)
                {
                    var type = func.Type as FunctionType;
                    if (type == null)
                    {
                        continue;
                    }

                    var call = new FunctionCall();
                    call.Functor = func;

                    var arglist  = new Expression[type.Parameters.Length];
                    var paramset = new HashSet <Parameter>(type.Parameters);

                    foreach (var arg in fncall.PositionalArguments)
                    {
                        var param = paramset.SingleOrDefault(p => p.Position == arg.Position);
                        if (param == null)
                        {
                            continue;
                        }

                        arglist[param.Position] = SelectFitting(
                            ConvertExpression(unit, scope, arg.Value),
                            param.Type);
                        if (arglist[param.Position] == null)
                        {
                            continue;
                        }

                        paramset.Remove(param);
                    }

                    foreach (var arg in fncall.NamedArguments)
                    {
                        var param = paramset.SingleOrDefault(p => p.Name == arg.Name);
                        if (param == null)
                        {
                            continue;
                        }

                        arglist[param.Position] = ConvertExpression(unit, scope, arg.Value).Single();
                        if (arglist[param.Position] == null)
                        {
                            continue;
                        }

                        paramset.Remove(param);
                    }

                    if (paramset.Count > 0)
                    {
                        continue;
                    }

                    call.Arguments = arglist;
                    results.Add(call);
                }
                if (results.Count == 0)
                {
                    throw Error.Critical($"Unknown function {fncall.Value} with fitting argument list.");
                }

                return(results);
            }
            else
            {
                throw new NotSupportedException($"The expression type '{value?.GetType()?.Name ?? "?"}' is not supported yet.");
            }
        }
Ejemplo n.º 2
0
 private SimpleScope(SimpleScope other)
 {
     this.symbols = new Dictionary <Signature, Symbol>(other.symbols);
 }