public void SpacesInArgListTest() { var mwb = MockWorkbook.standardMockWorkbook(); var e = mwb.envForSheet(1); var f = "=SUM(A1,A2,A3, A4)"; ExprOpt asto = Parcel.parseFormula(f, e.Path, e.WorkbookName, e.WorksheetName); string[] addrs = { "A1", "A2", "A3", "A4" }; var rng = Utility.makeUnionRangeFromA1Addrs(addrs, e); Expr[] a = { Expr.NewReferenceExpr(new AST.ReferenceRange(e, rng)) }; ArgList args = Utility.makeFSList(a); Expr correct = Expr.NewReferenceExpr(new AST.ReferenceFunction(e, "SUM", args, AST.Arity.VarArgs)); try { Expr ast = asto.Value; Assert.AreEqual(correct, ast); } catch (NullReferenceException nre) { Assert.Fail("Parse error: " + nre.Message); } }
public void UnaryPrecedenceTest() { var mwb = MockWorkbook.standardMockWorkbook(); var e = mwb.envForSheet(1); var f = "=-1 * 2"; ExprOpt asto = Parcel.parseFormula(f, e.Path, e.WorkbookName, e.WorksheetName); Expr correct = Expr.NewBinOpExpr( "*", Expr.NewUnaryOpExpr( '-', Expr.NewReferenceExpr(new AST.ReferenceConstant(e, 1.0)) ), Expr.NewReferenceExpr(new AST.ReferenceConstant(e, 2.0)) ); try { Expr ast = asto.Value; Assert.AreEqual(correct, ast); } catch (NullReferenceException nre) { Assert.Fail("Parse error: " + nre.Message); } }
public MethodInfo EmitLambda(string name, AST.DirectVarUse variable, AST.Expression /*!*/ expression, PhpTypeCode returnType) { MethodBuilder result = linqContextBuilder.DefineMethod(name, MethodAttributes.PrivateScope | MethodAttributes.SpecialName, PhpTypeCodeEnum.ToType(returnType), new Type[] { typeof(object) }); ILEmitter il = new ILEmitter(result); EnterLambdaDeclaration(il); if (variable != null) { // <variable> = COPY(<argument>); variable.Emit(cg); il.Emit(OpCodes.Ldarg_1); cg.EmitVariableCopy(CopyReason.Assigned, null); variable.EmitAssign(cg); } cg.EmitConversion(expression, returnType); il.Emit(OpCodes.Ret); LeaveLambdaDeclaration(); return(result); }
public Invocation GetCtorInvocation() { AST.Expression rr = LazyInit.VolatileRead(ref this.ctorInvocation); if (rr != null) { return(rr as Invocation); } else { int totalArgumentCount = unresolved.positionalArguments.Count + unresolved.namedCtorArguments.Count; AST.Expression[] arguments = new AST.Expression[totalArgumentCount]; string[] argumentNames = new string[totalArgumentCount]; int i = 0; while (i < unresolved.positionalArguments.Count) { IConstantValue cv = unresolved.positionalArguments[i]; arguments[i] = cv.ResolveConstant(context); i++; } foreach (var pair in unresolved.namedCtorArguments) { argumentNames[i] = pair.Key; arguments[i] = pair.Value.ResolveConstant(context); i++; } rr = NewExpression.ResolveObjectCreation(context, unresolved.loc, attributeType, arguments, argumentNames); return(LazyInit.GetOrSet(ref this.ctorInvocation, rr) as Invocation); } }
/// <summary> /// Applies the transformation /// </summary> /// <returns>The transformed item.</returns> /// <param name="el">The item to visit.</param> public virtual ASTItem Transform(ASTItem el) { if (el is AST.AssignmentExpression) { var ase = ((AST.AssignmentExpression)el); if (ase.Operator != AssignmentOperatorType.Assign) { AST.Expression clonedleft = ase.Left.Clone(); var newop = new AST.BinaryOperatorExpression() { Operator = ase.Operator.ToBinaryOperator(), Left = clonedleft, Right = ase.Right, Parent = ase, SourceExpression = ase.SourceExpression, SourceResultType = ase.SourceResultType }; newop.Left.Parent = newop.Right.Parent = newop; ase.Operator = AssignmentOperatorType.Assign; ase.Right = newop; return(null); } } return(el); }
public OverloadResolution PerformOverloadResolution(ICompilation compilation, Expression[] arguments, string[] argumentNames = null, bool allowExtensionMethods = true, bool allowExpandingParams = true, bool allowOptionalParameters = true, bool checkForOverflow = false, VSharpConversions conversions = null) { var typeArgumentArray = this.TypeArguments.ToArray(); OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, typeArgumentArray, conversions); or.AllowExpandingParams = allowExpandingParams; or.AllowOptionalParameters = allowOptionalParameters; or.CheckForOverflow = checkForOverflow; or.AddMethodLists(methodLists); if (allowExtensionMethods && !or.FoundApplicableCandidate) { // No applicable match found, so let's try extension methods. var extensionMethods = this.GetExtensionMethods(); if (extensionMethods.Any()) { Expression[] extArguments = new Expression[arguments.Length + 1]; extArguments[0] = new AST.Expression(this.TargetType); arguments.CopyTo(extArguments, 1); string[] extArgumentNames = null; if (argumentNames != null) { extArgumentNames = new string[argumentNames.Length + 1]; argumentNames.CopyTo(extArgumentNames, 1); } var extOr = new OverloadResolution(compilation, extArguments, extArgumentNames, typeArgumentArray, conversions); extOr.AllowExpandingParams = allowExpandingParams; extOr.AllowOptionalParameters = allowOptionalParameters; extOr.IsExtensionMethodInvocation = true; extOr.CheckForOverflow = checkForOverflow; foreach (var g in extensionMethods) { foreach (var method in g) extOr.AddCandidate(method); if (extOr.FoundApplicableCandidate) break; } // For the lack of a better comparison function (the one within OverloadResolution // cannot be used as it depends on the argument set): if (extOr.FoundApplicableCandidate || or.BestCandidate == null) { // Consider an extension method result better than the normal result only // if it's applicable; or if there is no normal result. or = extOr; } } } return or; }
static AST.Expression MapToNewContext(AST.Expression rr, ITypeResolveContext newContext) { if (rr is TypeOfExpression) { return new TypeOfExpression( rr.Type.ToTypeReference().Resolve(newContext), ((TypeOfExpression)rr).TargetType.ToTypeReference().Resolve(newContext)); } else if (rr is ArrayCreation) { ArrayCreation acrr = (ArrayCreation)rr; return new ArrayCreation( acrr.Type.ToTypeReference().Resolve(newContext), MapToNewContext(acrr.arguments, newContext), MapToNewContext(acrr.initializers != null ? acrr.initializers.Elements : null, newContext)); } else if (rr.IsCompileTimeConstant) { return Constant.CreateConstantFromValue(newContext.Compilation, rr.Type.ToTypeReference().Resolve(newContext), rr.ConstantValue, rr.Location); } else { return new ErrorExpression(rr.Type.ToTypeReference().Resolve(newContext)); } }
public void Arity2Test2() { var mwb = MockWorkbook.standardMockWorkbook(); var e = mwb.envForSheet(1); var f = "=SUMX2MY2(A$4:A$10,B$4:B$10)"; ExprOpt asto = Parcel.parseFormula(f, e.Path, e.WorkbookName, e.WorksheetName); Expr[] a = { Expr.NewReferenceExpr(new AST.ReferenceRange(e, Utility.makeRangeForA1("A$4:A$10", e))), Expr.NewReferenceExpr(new AST.ReferenceRange(e, Utility.makeRangeForA1("B$4:B$10", e))) }; ArgList args = Utility.makeFSList <AST.Expression>(a); Expr correct = Expr.NewReferenceExpr(new AST.ReferenceFunction(e, "SUMX2MY2", args, AST.Arity.NewFixed(2))); try { Expr ast = asto.Value; Assert.AreEqual(correct, ast); } catch (NullReferenceException nre) { Assert.Fail("Parse error: " + nre.Message); } }
public void basicIfExpression() { var mwb = MockWorkbook.standardMockWorkbook(); var e = mwb.envForSheet(1); string f = "=IF(TRUE, 1, 2)"; ExprOpt asto = Parcel.parseFormula(f, e.Path, e.WorkbookName, e.WorksheetName); Expr[] a = { Expr.NewReferenceExpr(new AST.ReferenceBoolean(e, true)), Expr.NewReferenceExpr(new AST.ReferenceConstant(e, 1.0)), Expr.NewReferenceExpr(new AST.ReferenceConstant(e, 2.0)) }; ArgList args = Utility.makeFSList <AST.Expression>(a); Expr correct = Expr.NewReferenceExpr(new AST.ReferenceFunction(e, "IF", args, AST.Arity.NewFixed(3))); try { Expr ast = asto.Value; Assert.AreEqual(ast, correct); } catch (NullReferenceException nre) { Assert.Fail("Parse error: " + nre.Message); } }
/// <summary> /// Initializes a new instance of the <see cref="T:SMEIL.Parser.AST.DataType"/> class. /// </summary> /// <param name="token">The source token.</param> /// <param name="width">The length of the array</param> /// <param name="SourceConstExpression">The constant expression defining the length of the array</param> /// <param name="elementType">The element type.</param> public DataType(ParseToken token, int width, Expression sourceConstExpr, DataType elementType) : base(token) { Type = ILType.Array; BitWidth = width; SourceConstExpression = sourceConstExpr; ElementType = elementType ?? throw new ArgumentNullException(nameof(elementType)); }
public virtual bool DoResolve(ResolveContext rc) { // DoResolve type definition AST.Expression rr = ResolveTypeDefinition(name, typeParameters.Count, rc); if (rr.IsError) { } return(true); }
private AST.Statement ParseSetVariableStatement(Lexer lexer) { var text = string.Empty; var token = lexer.InputToken(out text); if (token != Token.V名称符号) { throw new Exception($"语法错误,不是预期的输入:{text}。 在 {lexer.Line} 行,{lexer.Column}列"); } var name = ParseName(text); AST.Expression row = null; AST.Expression col = null; token = lexer.InputToken(out text); if (token == Token.的第) { row = ParseExpression(lexer); token = lexer.InputToken(out text); if (token != Token.行) { throw new Exception($"语法错误,不是预期的输入:{text}。 在 {lexer.Line} 行,{lexer.Column}列"); } col = ParseExpression(lexer); token = lexer.InputToken(out text); if (token != Token.列) { throw new Exception($"语法错误,不是预期的输入:{text}。 在 {lexer.Line} 行,{lexer.Column}列"); } token = lexer.InputToken(out text); } if (token != Token.的值为) { throw new Exception($"语法错误,不是预期的输入:{text}。 在 {lexer.Line} 行,{lexer.Column}列"); } token = lexer.InputToken(out text); if (token != Token.冒号) { lexer.ReturnToken(token, text); } var expr = ParseExpression(lexer); token = lexer.InputToken(out text); if (token != Token.的结果) { lexer.ReturnToken(token, text); } if (row != null || col != null) { return(new AST.SetArrayVariableStatement(name, row, col, expr)); } return(new AST.SetVariableStatement(name, expr)); }
static void WriteExpression(StreamWriter writer, AST.Expression expression) { if (expression is AST.Constant constant) { writer.Write(Assembly.format_constant, Assembly.register_A_32, constant.Value); } else if (expression is AST.UnaryOperator unary) { WriteUnaryOperator(writer, unary); } }
static AST.Expression[] MapToNewContext(IList<AST.Expression> input, ITypeResolveContext newContext) { if (input == null) return null; AST.Expression[] output = new AST.Expression[input.Count]; for (int i = 0; i < output.Length; i++) { output[i] = MapToNewContext(input[i], newContext); } return output; }
private AST.Expression ParseExpressionL1(Lexer lexer) { var lexpr = ParseExpressionL2(lexer); var text = string.Empty; var token = lexer.InputToken(out text); switch (token) { case Token.顿号: { var rexpr = ParseExpression(lexer); token = lexer.InputToken(out text); AST.Expression expr = null; switch (token) { case Token.相加: expr = new AST.AddExpression(lexpr, rexpr); break; case Token.相减: expr = new AST.SubExpression(lexpr, rexpr); break; case Token.相乘: expr = new AST.MulExpression(lexpr, rexpr); break; case Token.相除: expr = new AST.DivExpression(lexpr, rexpr); break; case Token.取余数: expr = new AST.ModExpression(lexpr, rexpr); break; default: throw new Exception($"语法错误,不是预期的输入:{text}。 在 {lexer.Line} 行,{lexer.Column}列"); } token = lexer.InputToken(out text); if (token != Token.的结果) { lexer.ReturnToken(token, text); } return(expr); } default: lexer.ReturnToken(token, text); break; } return(lexpr); }
/// <summary> /// Helper function that digs out the primitive expression underneath casting and formatting expressions /// </summary> /// <returns>The primitive expression or null.</returns> /// <param name="e">The expression to unwrap.</param> private static AST.PrimitiveExpression UnwrapPrimitive(AST.Expression e) { if (e is CustomNodes.ConversionExpression) { e = ((CustomNodes.ConversionExpression)e).Expression; } if (e is AST.PrimitiveExpression) { return((AST.PrimitiveExpression)e); } return(null); }
private AST.Statement ParseReturnStatement(Lexer lexer) { AST.Expression expr = null; var text = string.Empty; var token = lexer.InputToken(out text); lexer.ReturnToken(token, text); if (token != Token.行结束符号 && token != Token.段结束符号 && token != Token.V程序结束) { expr = ParseExpression(lexer); } return(new AST.ReturnStatement(expr)); }
IType[] OutputTypes(AST.Expression e, IType t) { // V# 4.0 spec: §7.5.2.4 Output types LambdaExpression lrr = e as LambdaExpression; if (lrr != null || e is MethodGroupExpression) { IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { return(new[] { m.ReturnType }); } } return(emptyTypeArray); }
private AST.Expression Multiplicative() { AST.Expression result = Unary(); while (true) { if (IsMatch(TokenType.STAR)) { result = new AST.BinaryExpression('*', result, Unary()); continue; } if (IsMatch(TokenType.SLASH)) { result = new AST.BinaryExpression('/', result, Unary()); continue; } break; } return(result); }
IType[] InputTypes(AST.Expression e, IType t) { // V# 4.0 spec: §7.5.2.3 Input types LambdaExpression lrr = e as LambdaExpression; if (lrr != null && lrr.IsImplicitlyTyped || e is MethodGroupExpression) { IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { IType[] inputTypes = new IType[m.Parameters.Count]; for (int i = 0; i < inputTypes.Length; i++) { inputTypes[i] = m.Parameters[i].Type; } return(inputTypes); } } return(emptyTypeArray); }
public static AST.Statement ParseStatement(Queue <Token> tokens) { Token token = tokens.Dequeue(); if (!Match(token, Token.TokenType.Keyword, Syntax.GetKeyword(Syntax.Keyword.Return))) { Fail(token); } AST.Expression expression = ParseExpression(tokens); AST.Statement statement = new AST.Return(expression); token = tokens.Dequeue(); if (token.Type != Token.TokenType.Semicolon) { Fail(token); } return(statement); }
private AST.Expression Additive() { AST.Expression result = Multiplicative(); while (true) { if (IsMatch(TokenType.PLUS)) { result = new AST.BinaryExpression('+', result, Multiplicative()); continue; } if (IsMatch(TokenType.MINUS)) { result = new AST.BinaryExpression('-', result, Multiplicative()); continue; } break; } return(result); }
public AST.Expression ResolveConstant(ITypeResolveContext context) { var csContext = (VSharpTypeResolveContext)context; if (context.CurrentAssembly != context.Compilation.MainAssembly) { // The constant needs to be resolved in a different compilation. IProjectContent pc = context.CurrentAssembly as IProjectContent; if (pc != null) { ICompilation nestedCompilation = context.Compilation.SolutionSnapshot.GetCompilation(pc); if (nestedCompilation != null) { var nestedContext = MapToNestedCompilation(csContext, nestedCompilation); AST.Expression rr = Constantify(new ResolveContext(nestedContext, CompilerContext.report)); return MapToNewContext(rr, context); } } } // Resolve in current context. return Constantify(new ResolveContext(csContext, CompilerContext.report)); }
public override Expression Resolve(ResolveContext resolver) { AliasNamespace target = new AliasNamespace(alias, Location); AST.Expression targetRR = target.Resolve(resolver); if (targetRR.IsError) { return(targetRR); } IList <IType> typeArgs = typeArgumentsrefs.Resolve(resolver.CurrentTypeResolveContext); if (LookForAttribute) { var wa = ResolveMemberAccess(resolver, targetRR, name + "Attribute", typeArgs, lookupMode); if (wa == null || wa.IsError) { wa = ResolveMemberAccess(resolver, targetRR, name, typeArgs, lookupMode); if (wa == null || wa.IsError) { resolver.Report.Error(6, loc, "This name `{0}' does not exist in the current context", name); } } LookForAttribute = false; return(wa); } else { var wa = ResolveMemberAccess(resolver, targetRR, name, typeArgs, lookupMode); if (wa == null || wa.IsError) { resolver.Report.Error(6, loc, "This name `{0}' does not exist in the current context", name); } return(wa); } }
public If(AST.Expression condition, Statements.Statement thenBranch, Statements.Statement ElseBranch) { this.Condition = condition; this.ThenBranch = thenBranch; this.ElseBranch = ElseBranch; }
private AST.Expression ParseExpressionL2(Lexer lexer) { AST.Expression expr = null; var text = string.Empty; var token = lexer.InputToken(out text); switch (token) { case Token.V名称符号: { var name = ParseName(text); token = lexer.InputToken(out text); if (token == Token.的第) { var row = ParseExpression(lexer); token = lexer.InputToken(out text); if (token != Token.行) { throw new Exception($"语法错误,不是预期的输入:{text}。 在 {lexer.Line} 行,{lexer.Column}列"); } var col = ParseExpression(lexer); token = lexer.InputToken(out text); if (token != Token.列) { throw new Exception($"语法错误,不是预期的输入:{text}。 在 {lexer.Line} 行,{lexer.Column}列"); } expr = new AST.ReadArrayVariableExpression(name, row, col); } else { lexer.ReturnToken(token, text); expr = new AST.ReadVariableExpression(name); } } break; case Token.V字符串值: { var svalue = ParseStringValue(text); expr = new AST.ConstantExpression(new StringValue(svalue)); } break; case Token.V数字值: { var dvalue = ParseNumberValue(text); if (((long)dvalue) == dvalue) { expr = new AST.ConstantExpression(new IntegerValue((long)dvalue)); } else { expr = new AST.ConstantExpression(new RealValue(dvalue)); } } break; case Token.真: { expr = new AST.ConstantExpression(new BooleanValue(true)); } break; case Token.假: { expr = new AST.ConstantExpression(new BooleanValue(false)); } break; case Token.方法调用: expr = ParseCallExpression(lexer, ParseName(text), false); break; default: throw new Exception($"语法错误,不是预期的输入:{text}。 在 {lexer.Line} 行,{lexer.Column}列"); } return(expr); }
/// <summary> /// Renders a single expression to VHDL /// </summary> /// <returns>The VHDL equivalent of the expression.</returns> /// <param name="expression">The expression to render</param> public string RenderExpression(AST.Expression expression) { if (expression is AST.ArrayCreateExpression) { return(RenderExpression(expression as ArrayCreateExpression)); } else if (expression is AST.EmptyArrayCreateExpression) { return(RenderExpression(expression as EmptyArrayCreateExpression)); } else if (expression is AST.AssignmentExpression) { return(RenderExpression(expression as AssignmentExpression)); } else if (expression is AST.BinaryOperatorExpression) { return(RenderExpression(expression as BinaryOperatorExpression)); } else if (expression is AST.CastExpression) { return(RenderExpression(expression as CastExpression)); } else if (expression is AST.CheckedExpression) { return(RenderExpression(expression as CheckedExpression)); } else if (expression is AST.ConditionalExpression) { return(RenderExpression(expression as ConditionalExpression)); } else if (expression is AST.EmptyExpression) { return(RenderExpression(expression as EmptyExpression)); } else if (expression is AST.IdentifierExpression) { return(RenderExpression(expression as IdentifierExpression)); } else if (expression is AST.IndexerExpression) { return(RenderExpression(expression as IndexerExpression)); } else if (expression is AST.InvocationExpression) { return(RenderExpression(expression as InvocationExpression)); } else if (expression is AST.MemberReferenceExpression) { return(RenderExpression(expression as MemberReferenceExpression)); } else if (expression is AST.MethodReferenceExpression) { return(RenderExpression(expression as MethodReferenceExpression)); } else if (expression is AST.ParenthesizedExpression) { return(RenderExpression(expression as ParenthesizedExpression)); } else if (expression is AST.PrimitiveExpression) { return(RenderExpression(expression as PrimitiveExpression)); } else if (expression is AST.UnaryOperatorExpression) { return(RenderExpression(expression as UnaryOperatorExpression)); } else if (expression is AST.UncheckedExpression) { return(RenderExpression(expression as UncheckedExpression)); } else { throw new Exception($"Unsupported expression type {expression.GetType().FullName}"); } }
public Expression(AST.Expression expression) { this.Expr = expression; }
/// <summary> /// Returns the VHDL type for an expression /// </summary> /// <returns>The VHDL type.</returns> /// <param name="element">The expression to get the type for.</param> public VHDLType VHDLType(AST.Expression element) { return(Parent.VHDLType(element)); }
/// <summary> /// Performs the type assignment to a process instance /// </summary> /// <param name="state">The validation state to use</param> /// <param name="instance">The process instance to use</param> private static void AssignProcessTypes(ValidationState state, Instance.IInstance parent, AST.Statement[] statements, Dictionary <Expression, DataType> assignedTypes) { // Get the scope for the intance var defaultScope = state.LocalScopes[parent]; // Extra expression that needs examining var extras = new AST.Expression[0].AsEnumerable(); if (parent is Instance.IDeclarationContainer pdecl1) { extras = extras.Concat( pdecl1.Declarations // Functions are handled elsewhere and have their own scopes .Where(x => !(x is AST.FunctionDefinition)) .SelectMany( x => x.All().OfType <AST.Expression>().Select(y => y.Current) ) ); } if (parent is Instance.IParameterizedInstance pp) { extras = extras.Concat( pp.MappedParameters .Select(x => x.MappedItem) .OfType <Instance.Bus>() .SelectMany(x => x.Instances .OfType <Instance.Signal>() .Select(y => y.Source.Initializer) .Where(y => y != null) ) ); } if (parent is Instance.IChildContainer ck) { extras = extras.Concat( ck.Instances .OfType <Instance.Bus>() .SelectMany(x => x.Instances .OfType <Instance.Signal>() .Select(y => y.Source.Initializer) .Where(y => y != null) ) ); } // List of statement expressions to examine for literal/constant type items var allExpressions = statements .All() .OfType <AST.Expression>() .Select(x => new { Item = x.Current, Scope = state.TryFindScopeForItem(x) ?? defaultScope }) .Concat(extras.Select(x => new { Item = x, Scope = defaultScope })) .Concat( extras .SelectMany(x => x.All().OfType <AST.Expression>().Select(y => y.Current)) .Select(x => new { Item = x, Scope = defaultScope }) ) .ToArray() .AsEnumerable(); // We use multiple iterations to assign types // The first iteration assigns types to all literal, bus, signal and variable expressions foreach (var nn in allExpressions) { var item = nn.Item; var scope = nn.Scope; // Skip duplicate assignments if (assignedTypes.ContainsKey(item)) { continue; } if (item is AST.LiteralExpression literal) { if (literal.Value is AST.BooleanConstant) { assignedTypes[literal] = new AST.DataType(literal.SourceToken, ILType.Bool, 1); } else if (literal.Value is AST.IntegerConstant) { assignedTypes[literal] = new AST.DataType(literal.SourceToken, ILType.SignedInteger, -1); } else if (literal.Value is AST.FloatingConstant) { assignedTypes[literal] = new AST.DataType(literal.SourceToken, ILType.Float, -1); } } else if (item is AST.NameExpression name) { var symbol = state.FindSymbol(name.Name, scope); var dt = FindDataType(state, name, scope); if (dt != null) { if (name.Name.Index.LastOrDefault() != null && dt.IsArray) { assignedTypes[name] = dt.ElementType; } else { assignedTypes[name] = dt; } if (parent is Instance.IParameterizedInstance ip) { state.RegisterItemUsageDirection(ip, symbol, ItemUsageDirection.Read, item); } } } } // Handle variables not used in normal expressions foreach (var item in statements.All().Select(x => x.Current)) { var scope = defaultScope; if (item is AST.AssignmentStatement assignmentStatement) { var symbol = state.FindSymbol(assignmentStatement.Name, scope); if (symbol is Instance.Variable var) { if (var.ResolvedType == null) { var.ResolvedType = state.ResolveTypeName(var.Source.Type, scope); } } else if (symbol is Instance.Signal sig) { if (sig.ResolvedType == null) { sig.ResolvedType = state.ResolveTypeName(sig.Source.Type, scope); } } else if (symbol == null) { throw new ParserException($"Symbol not found: \"{assignmentStatement.Name.AsString}\"", assignmentStatement.Name.SourceToken); } else { throw new ParserException($"Can only assign to signal or variable, {assignmentStatement.Name.AsString} is {symbol.GetType().Name}", assignmentStatement.Name.SourceToken); } } else if (item is AST.ForStatement forStatement) { var forScope = state.LocalScopes[forStatement]; var symbol = state.FindSymbol(forStatement.Variable.Name, forScope); if (symbol is Instance.Variable var) { if (var.ResolvedType == null) { var.ResolvedType = state.ResolveTypeName(var.Source.Type, scope); } } else if (symbol == null) { throw new ParserException($"Symbol not found: \"{forStatement.Variable.Name}\"", forStatement.Variable.SourceToken); } else { throw new ParserException($"Can only use variable as the counter in a for loop, {forStatement.Variable.Name} is {symbol.GetType().Name}", forStatement.Variable.SourceToken); } } } allExpressions = statements .All(AST.TraverseOrder.DepthFirstPostOrder) .OfType <AST.Expression>() .Select(x => new { Item = x.Current, Scope = state.TryFindScopeForItem(x) ?? defaultScope }) .Concat( extras .SelectMany(x => x.All(AST.TraverseOrder.DepthFirstPostOrder).OfType <AST.Expression>().Select(y => y.Current)) .Select(x => new { Item = x, Scope = defaultScope }) ) .Concat(extras.Select(x => new { Item = x, Scope = defaultScope })); // We are only concerned with expressions, working from leafs and up // At this point all literals, variables, signals, etc. should have a resolved type foreach (var nn in allExpressions) { var item = nn.Item; var scope = nn.Scope; // Skip duplicate assignments if (assignedTypes.ContainsKey(item)) { continue; } if (item is AST.UnaryExpression unaryExpression) { var sourceType = assignedTypes[unaryExpression.Expression]; switch (unaryExpression.Operation.Operation) { case AST.UnaryOperation.UnOp.LogicalNegation: if (!sourceType.IsBoolean) { throw new ParserException($"Cannot perform {unaryExpression.Operation.Operation} on {sourceType}", unaryExpression); } break; case AST.UnaryOperation.UnOp.Identity: case AST.UnaryOperation.UnOp.Negation: if (!sourceType.IsNumeric) { throw new ParserException($"Cannot perform {unaryExpression.Operation.Operation} on {sourceType}", unaryExpression); } break; case AST.UnaryOperation.UnOp.BitwiseInvert: if (!sourceType.IsInteger) { throw new ParserException($"Cannot perform {unaryExpression.Operation.Operation} on {sourceType}", unaryExpression); } break; default: throw new ParserException($"Unsupported unary operation: {unaryExpression.Operation.Operation}", unaryExpression); } // Unary operations do not change the type assignedTypes[item] = sourceType; } else if (item is AST.BinaryExpression binaryExpression) { var leftType = assignedTypes[binaryExpression.Left]; var rightType = assignedTypes[binaryExpression.Right]; // If we have a numerical operation, verify that the operands are numeric if (binaryExpression.Operation.IsNumericOperation) { if (!leftType.IsNumeric) { throw new ParserException($"The operand {binaryExpression.Left} must be numerical to be used with {binaryExpression.Operation.Operation}", binaryExpression.Left); } if (!rightType.IsNumeric) { throw new ParserException($"The operand {binaryExpression.Right} must be numerical to be used with {binaryExpression.Operation.Operation}", binaryExpression.Right); } } // If we have a logical operation, verify that the operands are boolean if (binaryExpression.Operation.IsLogicalOperation) { if (!leftType.IsBoolean) { throw new ParserException($"The operand {binaryExpression.Left} must be boolean to be used with {binaryExpression.Operation.Operation}", binaryExpression.Left); } if (!rightType.IsBoolean) { throw new ParserException($"The operand {binaryExpression.Right} must be boolean to be used with {binaryExpression.Operation.Operation}", binaryExpression.Right); } } // If we are doing a compare operation, verify that the types can be compared if (binaryExpression.Operation.IsEqualityOperation) { if (!state.CanEqualityCompare(leftType, rightType, scope)) { throw new ParserException($"Cannot perform boolean operation {binaryExpression.Operation.Operation} on types {leftType} and {rightType}", binaryExpression); } } // Special handling of bitshift, where the type of the shift count does not change they type on the input if (binaryExpression.Operation.Operation == BinOp.ShiftLeft || binaryExpression.Operation.Operation == BinOp.ShiftRight) { if (!leftType.IsInteger) { throw new ParserException($"The value being shifted must be an integer type but has type {leftType}", binaryExpression.Left); } if (!rightType.IsInteger) { throw new ParserException($"The shift operand must be an integer type but has type {rightType}", binaryExpression.Right); } assignedTypes[binaryExpression] = leftType; } else { // Make sure we can unify the types if (!state.CanUnifyTypes(leftType, rightType, scope)) { throw new ParserException($"The types types {leftType} and {rightType} cannot be unified for use with the operation {binaryExpression.Operation.Operation}", binaryExpression); } // Compute the unified type var unified = state.UnifiedType(leftType, rightType, scope); // If the source operands do not have the unified types, inject an implicit type-cast if (!object.Equals(leftType, unified)) { assignedTypes[binaryExpression.Left = new AST.TypeCast(binaryExpression.Left, unified, false)] = unified; } if (!object.Equals(rightType, unified)) { assignedTypes[binaryExpression.Right = new AST.TypeCast(binaryExpression.Right, unified, false)] = unified; } // Assign the type to this operation switch (binaryExpression.Operation.Operation) { // These operations just use the unified type case BinOp.Add: case BinOp.Subtract: case BinOp.Multiply: case BinOp.Divide: case BinOp.Modulo: case BinOp.BitwiseAnd: case BinOp.BitwiseOr: case BinOp.BitwiseXor: assignedTypes[binaryExpression] = unified; break; // These operations return a boolean result case BinOp.Equal: case BinOp.NotEqual: case BinOp.LessThan: case BinOp.LessThanOrEqual: case BinOp.GreaterThan: case BinOp.GreaterThanOrEqual: case BinOp.LogicalAnd: case BinOp.LogicalOr: assignedTypes[binaryExpression] = new AST.DataType(binaryExpression.SourceToken, ILType.Bool, 1); break; default: throw new ParserException($"Unable to handle operation: {binaryExpression.Operation.Operation}", binaryExpression); } } } else if (item is AST.TypeCast typecastExpression) { // Implicit typecasts are made by the parser so we do not validate those if (!typecastExpression.Explicit) { continue; } var sourceType = assignedTypes[typecastExpression.Expression]; var targetType = state.ResolveTypeName(typecastExpression.TargetName, scope); if (!state.CanTypeCast(sourceType, targetType, scope)) { throw new ParserException($"Cannot cast from {sourceType} to {typecastExpression.TargetName}", typecastExpression); } assignedTypes[typecastExpression] = targetType; } // Carry parenthesis expression types else if (item is AST.ParenthesizedExpression parenthesizedExpression) { assignedTypes[item] = assignedTypes[parenthesizedExpression.Expression]; } } // Then make sure we have assigned all targets foreach (var item in statements.All().OfType <AST.Statement>().Select(x => x.Current)) { var scope = defaultScope; if (item is AST.AssignmentStatement assignmentStatement) { var symbol = state.FindSymbol(assignmentStatement.Name, scope); var exprType = assignedTypes[assignmentStatement.Value]; DataType targetType; if (symbol is Instance.Variable variableInstance) { targetType = state.ResolveTypeName(variableInstance.Source.Type, scope); } else if (symbol is Instance.Signal signalInstance) { targetType = state.ResolveTypeName(signalInstance.Source.Type, scope); } else { throw new ParserException($"Assignment must be to a variable or a signal", item); } if (targetType.IsArray && assignmentStatement.Name.Index?.LastOrDefault() != null) { targetType = targetType.ElementType; } if (!state.CanUnifyTypes(targetType, exprType, scope)) { throw new ParserException($"Cannot assign \"{assignmentStatement.Value.SourceToken.Text}\" (with type {exprType}) to {assignmentStatement.Name.SourceToken} (with type {targetType})", item); } //var unified = state.UnifiedType(targetType, exprType, scope); // Force the right-hand side to be the type we are assigning to if (!object.Equals(exprType, targetType)) { // Make sure we do not loose bits with implicit typecasting if (exprType.BitWidth > targetType.BitWidth && targetType.BitWidth > 0) { throw new ParserException($"Assignment would loose precision from {exprType.BitWidth} bits to {targetType.BitWidth}", item); } assignedTypes[assignmentStatement.Value = new AST.TypeCast(assignmentStatement.Value, targetType, false)] = targetType; } if (parent is Instance.IParameterizedInstance ip) { state.RegisterItemUsageDirection(ip, symbol, ItemUsageDirection.Write, item); } } else if (item is AST.ForStatement forStatement) { var fromType = assignedTypes[forStatement.FromExpression]; var toType = assignedTypes[forStatement.ToExpression]; if (!fromType.IsInteger) { throw new ParserException("The from/to arguments in a for loop must be integer types", forStatement.FromExpression); } if (!toType.IsInteger) { throw new ParserException("The from/to arguments in a for loop must be integer types", forStatement.ToExpression); } var inttype = new DataType(forStatement.Variable.Name.SourceToken, ILType.SignedInteger, -1); if (fromType.BitWidth != -1) { assignedTypes[forStatement.FromExpression = new AST.TypeCast(forStatement.FromExpression, inttype, false)] = inttype; } if (toType.BitWidth != -1) { assignedTypes[forStatement.ToExpression = new AST.TypeCast(forStatement.ToExpression, inttype, false)] = inttype; } } } }