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; }
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; }
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; }
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 } }
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) }
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) ); } } }
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); }; }
/// <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); } } } }
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; }
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 ); } } } }
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); } }
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; }); }
public static BnfiTermConversion <TDOut> Cast <TDOut>(Terminal terminal) { return(Intro <TDOut>(terminal, (context, parseNode) => GrammarHelper.AstNodeToValue <TDOut>(parseNode.Token.Value), IdentityFunctionForceCast <TDOut, object>)); }
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) }
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; }
protected BnfiTermChoice(Type domainType, string name) : base(domainType, name) { GrammarHelper.MarkTransientForced(this); // the child node already contains the created ast node }
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; } }; }
protected BnfiTermConstant(Type domainType) : base(GrammarHelper.TypeNameWithDeclaringTypes(domainType)) { this.domainType = domainType; this.AstConfig.NodeCreator = (context, parseTreeNode) => parseTreeNode.AstNode = parseTreeNode.Token.Value; }