private HoconValue ResolveSubstitution(HoconSubstitution sub) { var subField = sub.ParentField; // first case, this substitution is a direct self-reference if (sub.Path == subField.Path) { var parent = sub.Parent; while (parent is HoconValue) { parent = parent.Parent; } // Fail case if (parent is HoconArray) { throw new HoconException("Self-referencing substitution may not be declared within an array."); } // try to resolve substitution by looking backward in the field assignment stack return(subField.OlderValueThan(sub)); } // second case, the substitution references a field child in the past if (sub.Path.IsChildPathOf(subField.Path)) { var olderValue = subField.OlderValueThan(sub); if (olderValue.Type == HoconType.Object) { var difLength = sub.Path.Count - subField.Path.Count; var deltaPath = sub.Path.SubPath(sub.Path.Count - difLength, difLength); var olderObject = olderValue.GetObject(); if (olderObject.TryGetValue(deltaPath, out var innerValue)) { return(innerValue.Type == HoconType.Object ? innerValue : null); } } } // Detect invalid parent-referencing substitution if (subField.Path.IsChildPathOf(sub.Path)) { throw new HoconException("Substitution may not reference one of its direct parents."); } // Detect invalid cyclic reference loop if (IsValueCyclic(subField, sub)) { throw new HoconException("A cyclic substitution loop is detected in the Hocon file."); } // third case, regular substitution _root.GetObject().TryGetValue(sub.Path, out var field); return(field?.Clone(field.Parent) as HoconValue); }
// The owner in this context can be either an object or an array. private void ParseObject(ref HoconValue owner) { if (owner == null) { throw HoconParserException.Create(_tokens.Current, Path, "ParseObject called with null parent."); } // Sanity check if (_tokens.Current.Type != TokenType.StartOfObject) { throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected `{TokenType.StartOfObject}`, found `{_tokens.Current.Type}` instead."); } // Consume curly bracket _tokens.ToNextSignificant(); var hoconObject = owner.GetObject(); if (hoconObject == null) { hoconObject = new HoconObject(owner); owner.Add(hoconObject); } var valueWasParsed = false; var parsing = true; while (parsing) { switch (_tokens.Current.Type) { case TokenType.Include: if (valueWasParsed) { throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}`, " + $"found `{_tokens.Current.Type}` instead."); } owner.ReParent(ParseInclude()); valueWasParsed = true; break; case TokenType.LiteralValue: if (_tokens.Current.IsNonSignificant()) { _tokens.ToNextSignificant(); } if (_tokens.Current.Type != TokenType.LiteralValue) { break; } if (valueWasParsed) { throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}`, " + $"found `{_tokens.Current.Type}` instead."); } ParseField(hoconObject); valueWasParsed = true; break; case TokenType.Comment: case TokenType.EndOfLine: valueWasParsed = false; _tokens.ToNextSignificantLine(); break; case TokenType.Comma: if (!valueWasParsed) { throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected `{TokenType.Assign}` or `{TokenType.StartOfObject}`, " + $"found `{_tokens.Current.Type}` instead."); } valueWasParsed = false; _tokens.ToNextSignificant(); break; case TokenType.EndOfObject: valueWasParsed = false; parsing = false; break; case TokenType.EndOfFile: throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected {TokenType.EndOfObject} but found {_tokens.Current.Type} instead."); default: throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Unexpected token `{_tokens.Current.Type}`."); } } // Consume the closing curly bracket. _tokens.ToNextSignificant(); }
private void ParseObject(HoconValue owner, bool root, string currentPath) { try { PushDiagnostics("{"); if (owner.IsObject()) { //the value of this KVP is already an object } else { //the value of this KVP is not an object, thus, we should add a new owner.NewValue(new HoconObject()); } HoconObject currentObject = owner.GetObject(); while (!_reader.EoF) { Token t = _reader.PullNext(); switch (t.Type) { case TokenType.Include: var included = _includeCallback(t.Value); var substitutions = included.Substitutions; foreach (var substitution in substitutions) { //fixup the substitution, add the current path as a prefix to the substitution path substitution.Path = currentPath + "." + substitution.Path; } _substitutions.AddRange(substitutions); var otherObj = included.Value.GetObject(); owner.GetObject().Merge(otherObj); break; case TokenType.EoF: if (!string.IsNullOrEmpty(currentPath)) { throw new HoconParserException(string.Format("Expected end of object but found EoF {0}", GetDiagnosticsStackTrace())); } break; case TokenType.Key: HoconValue value = currentObject.GetOrCreateKey(t.Value); var nextPath = currentPath == "" ? t.Value : currentPath + "." + t.Value; ParseKeyContent(value, nextPath); if (!root) { return; } break; case TokenType.ObjectEnd: return; } } } finally { PopDiagnostics(); } }