示例#1
0
        protected void ContractTo(BnfiTermCollection target)
        {
            if (!this.IsContractible)
            {
                GrammarHelper.ThrowGrammarErrorException(GrammarErrorLevel.Error, "This collection should not be a right-value: {0}", this.Name);
            }

            if (!this.listKind.HasValue)
            {
                GrammarHelper.ThrowGrammarErrorException(GrammarErrorLevel.Error, "Right-value collection has not been initialized: {0}", this.Name);
            }

            // note: target.RuleRaw is set and target.SetState is called by _MakePlusRule/_MakeStarRule
            if (this.listKind == ListKind.Plus)
            {
                _MakePlusRule(target, this.delimiter, this.element);
            }
            else if (this.listKind == ListKind.Star)
            {
                _MakeStarRule(target, this.delimiter, this.element);
            }
            else
            {
                throw new InvalidOperationException(string.Format("Unknown listKind: {0}", this.listKind));
            }

            this.RuleRaw = null;
            this.ClearState();
            this.hasBeenContracted = true;
        }
示例#2
0
        protected BnfiTermNonTerminal(Type domainType, string name)
            : this(name : name ?? GrammarHelper.TypeNameWithDeclaringTypes(domainType))
        {
            if (domainType == null)
            {
                throw new ArgumentNullException("domainType");
            }

            this.domainType      = domainType;
            this.hasExplicitName = name != null;
        }
示例#3
0
        protected void ContractTo(BnfiTermConversion target)
        {
            if (!this.IsContractible)
            {
                GrammarHelper.ThrowGrammarErrorException(GrammarErrorLevel.Error, "This value should not be a right-value: {0}", this.Name);
            }

            target.RuleRaw = this.RuleRaw;
            target.SetState(this);

            this.RuleRaw = null;
            this.ClearState();
            this.hasBeenContracted = true;
        }
示例#4
0
        internal void SetDecimalSeparatorOnNumberLiterals()
        {
            // see DefaultCulture's setter for more information
            if (Root == null)
            {
                return;
            }

            char numberDecimalSeparator = DefaultCulture.NumberFormat.NumberDecimalSeparator[0];    // NOTE: Irony handles numberDecimalSeparator only as character

            foreach (NumberLiteral numberLiteral in GrammarHelper.GetDescendantBnfTermsExcludingSelf(Root).OfType <NumberLiteral>())
            {
                numberLiteral.DecimalSeparator = numberDecimalSeparator;    // it seems this is the only way in Irony to set the DecimalSeparator which corresponds to DefaultCulture
            }
        }
示例#5
0
        protected BnfiTermCopy(Type domainType, BnfTerm bnfTerm, string name)
            : base(domainType, name: name ?? GetName(domainType, bnfTerm))
        {
            if (bnfTerm != null)
            {
                // "this" BnfiTermCopy is not an independent bnfTerm, just a syntax magic for BnfiTermRecord<TType> (we were called by the Copy method)
                this.IsContractible = true;
                this.RuleRaw        = bnfTerm.ToBnfExpression() + GrammarHelper.ReduceHere();
            }
            else
            {
                // "this" BnfiTermCopy is an independent bnfTerm
                this.IsContractible = false;
            }

            GrammarHelper.MarkTransientForced(this);    // default "transient" behavior (the Rule of this BnfiTermCopyable will contain the BnfiTerm... which actually does something)
        }
示例#6
0
        protected void CheckAfterRuleHasBeenSetThatChildrenAreNotContracted()
        {
            if (Rule != null)
            {
                var children = Rule.Data
                               .SelectMany(_children => _children)
                               .OfType <BnfiTermNonTerminal>();

                if (children.Any(child => child.hasBeenContracted))
                {
                    GrammarHelper.ThrowGrammarErrorException(
                        GrammarErrorLevel.Error,
                        "NonTerminal '{0}' has been contracted. You should use MakeUncontractible() on it.", children.First(child => child.hasBeenContracted)
                        );
                }
            }
        }
示例#7
0
        protected void SetNodeCreator <TCollectionStaticType, TElementStaticType>(Func <TCollectionStaticType> createCollection, Action <TCollectionStaticType, TElementStaticType> addElementToCollection)
        {
            this.AstConfig.NodeCreator = (context, parseTreeNode) =>
            {
                Lazy <TCollectionStaticType> collection = new Lazy <TCollectionStaticType>(() => createCollection());

                bool collectionHasElements = false;
                foreach (var element in GetFlattenedElements <TElementStaticType>(parseTreeNode, context))
                {
                    collectionHasElements = true;
                    addElementToCollection(collection.Value, element);
                }

                TCollectionStaticType astValue = !collectionHasElements && this.EmptyCollectionHandling == EmptyCollectionHandling.ReturnNull
                    ? default(TCollectionStaticType)
                    : collection.Value;

                parseTreeNode.AstNode = GrammarHelper.ValueToAstNode(astValue, context, parseTreeNode);
            };
        }
示例#8
0
        /// <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);
        }
示例#9
0
        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);
                    }
                }
            }
        }
示例#10
0
        protected BnfiTermConversion(Type domainType, BnfTerm bnfTerm, ValueIntroducer <object> valueIntroducer, ValueConverter <object, object> inverseValueConverterForUnparse,
                                     object defaultValue, bool isOptionalValue, string name, bool astForChild)
            : base(domainType, name)
        {
            this.IsContractible  = true;
            this.bnfTerm         = bnfTerm;
            this.isOptionalValue = isOptionalValue;
            this.defaultValue    = defaultValue;

            if (!astForChild)
            {
                bnfTerm.SetFlag(TermFlags.NoAstNode);
            }

            this.RuleRawWithMove = isOptionalValue
                ? GrammarHelper.PreferShiftHere() + bnfTerm | Irony.Parsing.Grammar.CurrentGrammar.Empty
                : bnfTerm.ToBnfExpression();

            this.AstConfig.NodeCreator =
                (context, parseTreeNode) =>
            {
                try
                {
                    parseTreeNode.AstNode = GrammarHelper.ValueToAstNode(valueIntroducer(context, new ParseTreeNodeWithoutAst(parseTreeNode)), context, parseTreeNode);
                }
                catch (AstException e)
                {
                    context.AddMessage(AstException.ErrorLevel, parseTreeNode.Span.Location, e.Message);
                }
                catch (FatalAstException e)
                {
                    context.AddMessage(FatalAstException.ErrorLevel, parseTreeNode.Span.Location, e.Message); // although it will be abandoned anyway
                    e.Location = parseTreeNode.Span.Location;
                    throw;                                                                                    // handle in MultiParser
                }
            };

            this.inverseValueConverterForUnparse = inverseValueConverterForUnparse;
        }
示例#11
0
        private void ProcessUnparseHints(BnfExpression rule)
        {
            if (rule != null)
            {
                for (int childBnfTermListIndex = 0; childBnfTermListIndex < rule.Data.Count; childBnfTermListIndex++)
                {
                    BnfTermList bnfTermList = rule.Data[childBnfTermListIndex];

                    try
                    {
                        UnparseHint unparseHint = (UnparseHint)bnfTermList.SingleOrDefault(bnfTerm => bnfTerm is UnparseHint);
                        childBnfTermListIndexToUnparseHint.Add(childBnfTermListIndex, unparseHint);
                    }
                    catch (InvalidOperationException)
                    {
                        GrammarHelper.ThrowGrammarErrorException(
                            GrammarErrorLevel.Error,
                            "NonTerminal '{0}' has more than one UnparseHint on its {1}. childrenlist. Only one UnparseHint is allowed per childrenlist.", this, childBnfTermListIndex + 1
                            );
                    }
                }
            }
        }
示例#12
0
        private void RegisterNonTerminalOperator(NonTerminal nonTerminalOperator, int precedence, Associativity?associativity, bool recurse)
        {
            if (nonTerminalOperator.Precedence != BnfTerm.NoPrecedence)
            {
                throw new InvalidOperationException(string.Format("Double call of RegisterOperators on non-terminal '{0}'", nonTerminalOperator));
            }

            if (nonTerminalOperator.Rule == null)
            {
                GrammarHelper.ThrowGrammarErrorException(GrammarErrorLevel.Error,
                                                         "Rule is needed to have been set for nonterminal operator '{0}' before calling RegisterOperators", nonTerminalOperator);
            }

            if (recurse)
            {
                foreach (var bnfTerms in nonTerminalOperator.Rule.GetBnfTermsList())
                {
                    foreach (var bnfTerm in bnfTerms)
                    {
                        if (bnfTerm is Terminal)
                        {
                            RegisterTerminalOperator((Terminal)bnfTerm, precedence, associativity, recurse);
                        }
                        else if (bnfTerm is NonTerminal && !bnfTerm.Flags.IsSet(TermFlags.NoAstNode))
                        {
                            RegisterNonTerminalOperator((NonTerminal)bnfTerm, precedence, associativity, recurse);
                        }
                    }
                }

                nonTerminalOperator.SetFlag(TermFlags.InheritPrecedence);
            }
            else
            {
                BaseRegisterOperator(nonTerminalOperator, precedence, associativity);
            }
        }
示例#13
0
        private static ValueIntroducer <TDOut> ConvertValueConverterToValueIntroducer <TDIn, TDOut>(ValueConverter <TDIn, TDOut> valueConverter, bool isOptionalValue, TDOut defaultValue)
        {
            return((context, parseTreeNode) =>
            {
                Func <IEnumerable <ParseTreeNode>, Func <ParseTreeNode, bool>, ParseTreeNode> chooser;
                if (isOptionalValue)
                {
                    chooser = Enumerable.SingleOrDefault <ParseTreeNode>;
                }
                else
                {
                    chooser = Enumerable.Single <ParseTreeNode>;
                }

                ParseTreeNode parseTreeChild;

                try
                {
                    parseTreeChild = chooser(parseTreeNode.ChildNodes, childNode => childNode.AstNode != null);
                }
                catch (InvalidOperationException)
                {
                    if (isOptionalValue)
                    {
                        throw new ArgumentException("Only zero or one child with ast node is allowed for an optional BnfiTermConversion term: {0}", parseTreeNode.Term.Name);
                    }
                    else
                    {
                        throw new ArgumentException("Exactly one child with ast node is allowed for a non-optional BnfiTermConversion term: {0}", parseTreeNode.Term.Name);
                    }
                }

                return parseTreeChild != null
                    ? valueConverter(GrammarHelper.AstNodeToValue <TDIn>(parseTreeChild.AstNode))
                    : defaultValue;
            });
        }
示例#14
0
 public static BnfiTermConversion <TDOut> Cast <TDOut>(Terminal terminal)
 {
     return(Intro <TDOut>(terminal, (context, parseNode) => GrammarHelper.AstNodeToValue <TDOut>(parseNode.Token.Value), IdentityFunctionForceCast <TDOut, object>));
 }
示例#15
0
 protected BnfiTermConversion(Type domainType, string name)
     : base(domainType, name)
 {
     this.inverseValueConverterForUnparse = IdentityFunction;
     GrammarHelper.MarkTransientForced(this);    // default "transient" behavior (the Rule of this BnfiTermConversion will contain the BnfiTermConversion which actually does something)
 }
示例#16
0
 protected Member(MemberInfo memberInfo, BnfTerm bnfTerm)
     : base(name: string.Format("{0}.{1}", GrammarHelper.TypeNameWithDeclaringTypes(memberInfo.DeclaringType), memberInfo.Name.ToLower()))
 {
     this.MemberInfo = memberInfo;
     this.BnfTerm    = bnfTerm;
 }
示例#17
0
 protected BnfiTermChoice(Type domainType, string name)
     : base(domainType, name)
 {
     GrammarHelper.MarkTransientForced(this);      // the child node already contains the created ast node
 }
示例#18
0
        protected BnfiTermRecord(Type domainType, string name)
            : base(domainType, name)
        {
#if !WINDOWS_STORE
#if PCL
            if (domainType.GetConstructor(new Type[0]) == null)
#else
            if (domainType.GetConstructor(bindingAttrInstanceAll, binder: null, types: Type.EmptyTypes, modifiers: null) == null)
#endif
            { throw new ArgumentException("Type has no default constructor (neither public nor nonpublic)", "type"); }
#endif

            this.AstConfig.NodeCreator = (context, parseTreeNode) =>
            {
                try
                {
                    object astValue;
                    try
                    {
#if PCL
                        astValue = ActivatorEx.CreateInstance(domainType, nonPublic: true);
#else
                        astValue = Activator.CreateInstance(domainType, nonPublic: true);
#endif
                    }
                    catch (MissingMemberException)
                    {
                        throw new AstException(string.Format("Type '{0}' does not have a parameterless public or internal constructor", domainType.FullName));
                    }

                    var parseChildBnfTerms = parseTreeNode.ChildNodes.Select(childParseTreeNode => childParseTreeNode.Term).ToList();

                    var parseChildValues = parseTreeNode.ChildNodes
                                           .Select(
                        (parseChildNode, parseChildNodeIndex) => new
                    {
                        ReferredBnfTerm = new ReferredBnfTermEL(parseChildBnfTerms, parseChildNode.Term, parseChildNodeIndex),
                        Value           = GrammarHelper.AstNodeToValue(parseChildNode.AstNode)
                    }
                        )
                                           .Where(parseChildValue => parseChildValue.Value != null)
                                           .ToList();

                    // 1. memberwise copy for BnfiTermCopy items
                    foreach (var parseChildValue in parseChildValues)
                    {
                        if (!IsMemberAtParse(parseChildValue.ReferredBnfTerm) && IsMemberwiseCopyable(astValue, parseChildValue.Value))
                        {
                            MemberwiseCopyExceptNullAndEmptyCollectionValues(astValue, parseChildValue.Value);
                        }
                    }

                    // 2. set member values for member items (it's the second step, so that we can overwrite the copied members if we want)
                    foreach (var parseChildValue in parseChildValues)
                    {
                        if (IsMemberAtParse(parseChildValue.ReferredBnfTerm))
                        {
                            SetValue(GetMemberAtParse(parseChildValue.ReferredBnfTerm).MemberInfo, astValue, parseChildValue.Value);
                        }
                    }

                    parseTreeNode.AstNode = GrammarHelper.ValueToAstNode(astValue, context, parseTreeNode);
                }
                catch (AstException e)
                {
                    context.AddMessage(AstException.ErrorLevel, parseTreeNode.Span.Location, e.Message);
                }
                catch (FatalAstException e)
                {
                    context.AddMessage(FatalAstException.ErrorLevel, parseTreeNode.Span.Location, e.Message);   // although it will be abandoned anyway
                    e.Location = parseTreeNode.Span.Location;
                    throw;
                }
            };
        }
示例#19
0
 protected BnfiTermConstant(Type domainType)
     : base(GrammarHelper.TypeNameWithDeclaringTypes(domainType))
 {
     this.domainType            = domainType;
     this.AstConfig.NodeCreator = (context, parseTreeNode) => parseTreeNode.AstNode = parseTreeNode.Token.Value;
 }