private void MapProperty(PropertyInfo property, object astNode, ICstNode newCstNode) { /* * else if (consumeAttribute.Converter != null) * { * CoalesceAndConvert(astNode, property, newCstNode, consumeAttribute.Converter, consumeAttribute.ConverterArgs); * } */ // If the property type is simple, we will just convert the text of the CST node into that type. if (IsSimpleType(property.PropertyType)) { Coalesce(astNode, property, newCstNode, property.PropertyType); } // Lists are a special case to help unravel recursion. else if (property.PropertyType.IsGenericList()) { BuildList(property, astNode, (CstNonterminalNode)newCstNode); } // Each enum value may contain a ConsumeAttribute indicating how to map it. Otherwise we do // a simple parse. else if (property.PropertyType.IsEnum) { var enums = property.PropertyType.GetEnums(); string expectedName = newCstNode.Coalesce(); foreach (var @enum in enums) { string name = @enum.Name; ConsumeAttribute[] enumConsumeAttributes = @enum.Field.GetAttributes <ConsumeAttribute>(); if (enumConsumeAttributes.Length > 0) { foreach (ConsumeAttribute enumConsume in enumConsumeAttributes) { if (enumConsume.Expression == expectedName) { name = enumConsume.Expression; break; } } } if (name == expectedName) { property.SetValue(astNode, @enum.Value, null); goto foundEnum; } } throw new InvalidOperationException("No enum found for " + expectedName + " in " + property.PropertyType); foundEnum: ; } // Otherwise we create a new AST node and recurse into this method. else //if (nonterminalTypes.Values.Contains(property.PropertyType)) { object newAstNode = Build(property.PropertyType, newCstNode); // Set the property to the new AST node property.SetValue(astNode, newAstNode, null); } }
private void Coalesce(object astNode, PropertyInfo property, ICstNode cstNode, Type targetType) { string value = cstNode.Coalesce(); object o; try { o = Convert.ChangeType(value, targetType); } catch (Exception e) { throw new InvalidOperationException("Error converting '" + value + "' to a " + targetType.FullName + " for property " + property.GetPath(), e); } property.SetValue(astNode, o, null); }
/// <summary> /// /// </summary> /// <param name="astNodeType">The type of node we are creating. A subclass might possibly be selected.</param> /// <param name="cstNode">The CST node that represents the structured input for the specified AST node</param> /// <returns>The new astNode of the specified astNodeType that consumes the cstNode</returns> private object Build(Type astNodeType, ICstNode cstNode) { int consumeIndex = 0; if (astNodeType.IsAbstract || astNodeType.IsInterface) { string ruleName = ((CstNonterminalNode)cstNode).Nonterminal.Name; CstNonterminalNode cstNonterminalNode = (CstNonterminalNode)cstNode; astNodeType = GetSubclass(ref cstNonterminalNode, ruleName); cstNode = cstNonterminalNode; } object astNode; try { if (astNodeType == typeof(string)) { return(cstNode.Coalesce()); } else if (astNodeType == typeof(int)) { return(int.Parse(cstNode.Coalesce())); } else if (astNodeType == typeof(byte)) { return(byte.Parse(cstNode.Coalesce())); } else if (astNodeType == typeof(short)) { return(short.Parse(cstNode.Coalesce())); } else if (astNodeType == typeof(long)) { return(long.Parse(cstNode.Coalesce())); } else if (astNodeType == typeof(float)) { return(float.Parse(cstNode.Coalesce())); } else if (astNodeType == typeof(double)) { return(double.Parse(cstNode.Coalesce())); } else if (astNodeType == typeof(decimal)) { return(decimal.Parse(cstNode.Coalesce())); } else if (astNodeType == typeof(bool)) { return(bool.Parse(cstNode.Coalesce())); } else { astNode = CreateAstNode(astNodeType); } } catch (Exception e) { throw new InvalidOperationException("Error instantiating " + astNodeType.FullName, e); } foreach (PropertyInfo property in astNode.GetType().GetAllPropertiesInAncestry()) { ConsumeAttribute[] consumeAttributes = property.GetAttributes <ConsumeAttribute>(); foreach (ConsumeAttribute consumeAttribute in consumeAttributes) { if (consumeIndex > 0 && consumeIndex != consumeAttribute.Production) { continue; } CstNonterminalNode productionNode = cstNode as CstNonterminalNode; // 1) Handle the simple case where the NonTerminal exactly matches the NonTerminal property of our consume attribute // 2) Handle the empty [Consume] case, which means it goes into this block no matter what. // Get the expression (parsed from the NonTerminal property) ConsumeExpression expression = consumeAttribute.GetExpression(); // The goal of the expression is to bring us to a new CST node (a descendent of the current one) ICstNode newCstNode; try { newCstNode = expression != null?expression.Resolve(productionNode).FirstOrDefault() : productionNode; } catch (Exception e) { throw new InvalidOperationException("Error resolving expression '" + consumeAttribute.Expression + "' for " + property.GetPath(), e); } // There are a number of reasons why the new cst node might be null. It is perfectly // legal to specify nonterminal expressions that only resolve under certain productions. // In other scenarios, it will remain null. if (newCstNode != null) { // If the Value property is set, then we simply assign the property to that value (since newCstNode is not null) if (consumeAttribute.Value != null) { property.SetValue(astNode, consumeAttribute.Value, null); } else if (consumeAttribute.Type != null) { Coalesce(astNode, property, newCstNode, consumeAttribute.Type); } else { MapProperty(property, astNode, newCstNode); } goto nextProperty; } } var impliedCstNode = ((CstNonterminalNode)cstNode).Children.OfType <ICstNonterminalNode>().SingleOrDefault(o => o.Nonterminal.Name == property.Name); if (impliedCstNode != null) { MapProperty(property, astNode, impliedCstNode); } nextProperty :; } return(astNode); }