/// <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."); } }
private SimpleScope(SimpleScope other) { this.symbols = new Dictionary <Signature, Symbol>(other.symbols); }