/// <summary> /// Resolves a symbol to be a variable or a bus /// </summary> /// <param name="expression">The expression to resolve</param> /// <param name="scope">The scope to use</param> /// <returns>The resolved item or <c>null<c/></returns> public Instance.IInstance ResolveSymbol(Expression expression, ScopeState scope) { if (expression is NameExpression name) { var symbol = FindSymbol(name.Name, scope); if (symbol is Instance.IInstance || symbol == null) { return((Instance.IInstance)symbol); } // We no longer quick-patch for constant declarations, but require that they are all inserted as instances // if (symbol is AST.ConstantDeclaration cdecl) // return new Instance.ConstantReference(cdecl); // We need to be able to determine array lengths and others when creating the exports if (scope == m_rootscope && symbol is AST.ConstantDeclaration cdecl) { return(new Instance.ConstantReference(cdecl)); } throw new ParserException($"Got element of type {symbol.GetType().Name} but expected an instance", expression); } else if (expression is LiteralExpression literal) { return(new Instance.Literal(literal.Value)); } else { throw new ParserException($"Expression not supported for binding parameters", expression); } }
/// <summary> /// Takes an instance and reduces it to an integer, or throws an exception if this is not possible /// </summary> /// <param name="source">The source line, used to give indicative error messags</param> /// <param name="instance">The instance to reduce</param> /// <param name="scope">The scope to use</param> /// <returns>An integer</returns> public int ResolveToInteger(ParsedItem source, IInstance instance, ScopeState scope) { if (instance is Instance.ConstantReference constDecl) { var dt = ResolveTypeName(constDecl.Source.DataType, scope); if (dt == null) { throw new ParserException($"Failed to resolve data type: {constDecl.Source.DataType}", source); } if (!dt.IsInteger) { throw new ParserException($"Cannot use item of type {constDecl.Source.DataType} as an integer is required", source); } return(((AST.IntegerConstant)((AST.LiteralExpression)constDecl.Source.Expression).Value).ToInt32); } else if (instance is Instance.Literal lit) { if (!(lit.Source is AST.IntegerConstant intConst)) { throw new ParserException($"Cannot use literal of type {lit.Source} as an integer is required", source); } return(intConst.ToInt32); } throw new ParserException($"Must use a constant or literal integer value, got {instance}", source); }
/// <summary> /// Starts a new scope using this as the base scope /// </summary> /// <param name="items">The items to register for the scope /// <returns>A disposable that will unset the current scope</returns> public ScopeState StartScope(params object[] items) { var sc = new ScopeState(SymbolScopes); if (items != null) { foreach (var item in items.Where(x => x != null)) { LocalScopes[item] = sc; } } return(sc); }
/// <summary> /// The instances /// </summary> /// <param name="state">The validation state</param> /// <param name="instances">The instances to assign types to</param> /// <param name="scope">The scope to use for lookup</param> private void ResolveTypes(ValidationState state, IEnumerable <Instance.IInstance> instances, ScopeState scope) { foreach (var r in instances) { if (r is Instance.ConstantReference cref) { if (cref.ResolvedType == null) { cref.ResolvedType = state.ResolveTypeName(cref.Source.DataType, scope); } } else if (r is Instance.Variable v) { if (v.ResolvedType == null) { v.ResolvedType = state.ResolveTypeName(v.Source.Type, scope); } } } }
/// <summary> /// Finds the item with the given name /// </summary> /// <param name="name">The name to look for</param> /// <param name="scope">The scope to use</param> /// <returns>The item matching the name, or null</returns> public object FindSymbol(AST.Identifier name, ScopeState scope) { return(FindSymbol(new AST.Name(name.SourceToken, new [] { name ?? throw new ArgumentNullException(nameof(name)) }, null), scope));
/// <summary> /// Finds the item with the given name /// </summary> /// <param name="name">The name to look for</param> /// <param name="scope">The scope to use</param> /// <returns>The item matching the name, or null</returns> public object FindSymbol(string name, ScopeState scope) { return(FindSymbol(AsName(name), scope)); }
/// <summary> /// Resolves an expression to an integer constant, or throws an exception if this is not possible /// </summary> /// <param name="expression">The expression to resolve</param> /// <param name="scope">The scope to use</param> /// <returns>An integer</returns> public int ResolveToInteger(Expression expression, ScopeState scope) { return(ResolveToInteger(expression, ResolveSymbol(expression, scope), scope)); }
/// <summary> /// Creates a new validation state shadowing the /// </summary> public ValidationState() { m_rootscope = new ScopeState(SymbolScopes); }
/// <summary> /// Performs a lookup to find the symbol and returns the datatype of the found symbol /// </summary> /// <param name="state">The current state</param> /// <param name="scope">The scope to use</param> /// <param name="name">The name to find</param> /// <returns>The datatype or <c>null</c></returns> private static DataType FindDataType(Validation.ValidationState state, AST.NameExpression name, ScopeState scope) { var symb = state.ResolveSymbol(name, scope); if (symb == null) { throw new ParserException($"Unable to find instance for name: {name.Name}", name); } if (symb is Instance.Variable variable) { return(variable.ResolvedType = state.ResolveTypeName(variable.Source.Type, scope)); } else if (symb is Instance.Bus bus) { return(state.ResolveBusSignalTypes(bus, scope)); } else if (symb is Instance.Signal signal) { return(signal.ResolvedType = state.ResolveTypeName(signal.Source.Type, scope)); } else if (symb is Instance.ConstantReference constant) { return(constant.ResolvedType = state.ResolveTypeName(constant.Source.DataType, scope)); } else if (symb is Instance.EnumFieldReference efr) { return(new AST.DataType(name.SourceToken, efr.ParentType.Source)); } else if (symb is Instance.Literal literalInstance) { if (literalInstance.Source is AST.BooleanConstant) { return(new AST.DataType(literalInstance.Source.SourceToken, ILType.Bool, 1)); } else if (literalInstance.Source is AST.IntegerConstant) { return(new AST.DataType(literalInstance.Source.SourceToken, ILType.SignedInteger, -1)); } else if (literalInstance.Source is AST.FloatingConstant) { return(new AST.DataType(literalInstance.Source.SourceToken, ILType.Float, -1)); } } return(null); }