/// <summary> /// Practically the same as marking with MarkTransient. It is used in those cases when MarkTransient would not work due to technical issues, /// or when there are multiple children and only one of them has ast node. /// </summary> /// <example> /// When creating a Member we should not use MarkTransient, because under this Member there can be a list (TermFlags.IsList), /// which makes this term to become a list container (TermFlags.IsListContainer), and this causes ReduceParserActionCreate to process this term /// with ReduceListContainerParserAction instead of the desired ReduceTransientParserAction, which causes the parseTreeNode of this term /// to remain in the parseTree despite it is being transient (TermFlags.IsTransient), and this results bad behavior when building the AST tree, /// because this term will not produce an ast node (TermFlags.NoAstNode), and therefore the AST builder will not process its children. /// </example> internal static void MarkTransientForced(NonTerminal nonTerminal) { nonTerminal.AstConfig.NodeCreator = (context, parseTreeNode) => { try { parseTreeNode.AstNode = parseTreeNode.ChildNodes.Single(childNode => childNode.AstNode != null).AstNode; } catch (InvalidOperationException) { // throw exception only if this exception cannot be the consequence of another ast error if (!GrammarHelper.HasError(context)) { throw new ArgumentException(string.Format("Only one child with astnode is allowed for a forced transient node: {0}", parseTreeNode.Term.Name), "nonTerminal"); } } }; nonTerminal.SetFlag(TermFlags.InheritPrecedence); }
protected IEnumerable <TElementStaticType> GetFlattenedElements <TElementStaticType>(ParseTreeNode parseTreeNode, AstContext context) { foreach (var parseTreeChild in parseTreeNode.ChildNodes) { /* * The type of childValue is 'object' because childValue can be other than an element, which causes error, * but we want to give a proper error message (see below) instead of throwing a simple cast exception * */ object childValue = GrammarHelper.AstNodeToValue(parseTreeChild.AstNode); if (domainElementType.IsInstanceOfType(childValue)) { yield return((TElementStaticType)childValue); } else if (parseTreeChild.Term.Flags.IsSet(TermFlags.IsList)) { foreach (var descendantElement in GetFlattenedElements <TElementStaticType>(parseTreeChild, context)) { yield return(descendantElement); } } else if (parseTreeChild.Term.Flags.IsSet(TermFlags.NoAstNode)) { // simply omit children with no ast (they are probably delimiters) } else { // throw exception only if this situation cannot be the consequence of another ast error if (!GrammarHelper.HasError(context)) { string errorMessage = string.Format("Term '{0}' should be type of '{1}' but found '{2}' instead", parseTreeChild.Term, domainElementType.FullName, childValue != null ? childValue.GetType().FullName : "<<NULL>>"); throw new InvalidOperationException(errorMessage); } } } }