Пример #1
0
        internal void ResolveValue(HoconSubstitution child)
        {
            if (child.Type == HoconType.Empty)
            {
                Remove(child);
            }
            else
            {
                if (Type == HoconType.Empty)
                {
                    Type = child.Type;
                }
                else if (Type != child.Type)
                {
                    throw HoconParserException.Create(child, child.Path,
                                                      "Invalid substitution, substituted type must match its sibling type. " +
                                                      $"Sibling type:{Type}, substitution type:{child.Type}");
                }
            }

            switch (Parent)
            {
            case HoconField v:
                v.ResolveValue(this);
                break;

            case HoconArray a:
                a.ResolveValue(child);
                break;

            default:
                throw new Exception($"Invalid parent type while resolving substitution:{Parent.GetType()}");
            }
        }
Пример #2
0
        private HoconArray ParsePlusEqualAssignArray(IHoconElement owner)
        {
            // sanity check
            if (_tokens.Current.Type != TokenType.PlusEqualAssign)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  "Failed to parse Hocon field with += operator. " +
                                                  $"Expected {TokenType.PlusEqualAssign}, found {{_tokens.Current.Type}} instead.");
            }

            var currentArray = new HoconArray(owner);

            // consume += operator token
            ConsumeWhitelines();

            switch (_tokens.Current.Type)
            {
            case TokenType.Include:
                currentArray.Add(ParseInclude(currentArray));
                break;

            case TokenType.StartOfArray:
                // Array inside of arrays are parsed as values because it can be value concatenated with another array.
                currentArray.Add(ParseValue(currentArray));
                break;

            case TokenType.StartOfObject:
                currentArray.Add(ParseObject(currentArray));
                break;

            case TokenType.LiteralValue:
                if (_tokens.Current.IsNonSignificant())
                {
                    ConsumeWhitelines();
                }
                if (_tokens.Current.Type != TokenType.LiteralValue)
                {
                    break;
                }

                currentArray.Add(ParseValue(currentArray));
                break;

            case TokenType.SubstituteOptional:
            case TokenType.SubstituteRequired:
                var pointerPath       = HoconPath.Parse(_tokens.Current.Value);
                HoconSubstitution sub = new HoconSubstitution(currentArray, pointerPath, _tokens.Current,
                                                              _tokens.Current.Type == TokenType.SubstituteRequired);
                _substitutions.Add(sub);
                currentArray.Add(sub);
                _tokens.Next();
                break;

            default:
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Failed to parse Hocon array. Expected {TokenType.EndOfArray} but found {_tokens.Current.Type} instead.");
            }

            return(currentArray);
        }
Пример #3
0
        internal void ResolveValue(HoconSubstitution child)
        {
            var index = IndexOf(child);

            Remove(child);

            if (child.Type != HoconType.Empty)
            {
                if (Type == HoconType.Empty)
                {
                    Type = child.Type;
                }
                else if (!Type.IsMergeable(child.Type))
                {
                    throw HoconParserException.Create(child, child.Path,
                                                      "Invalid substitution, substituted type be must be mergeable with its sibling type. " +
                                                      $"Sibling type:{Type}, substitution type:{child.Type}");
                }

                var clonedValue = (HoconValue)child.ResolvedValue.Clone(Parent);
                switch (Type)
                {
                case HoconType.Object:
                    Insert(index, clonedValue.GetObject());
                    break;

                case HoconType.Array:
                    var hoconArray = new HoconArray(this);
                    hoconArray.AddRange(clonedValue.GetArray());
                    Insert(index, hoconArray);
                    break;

                case HoconType.Boolean:
                case HoconType.Number:
                case HoconType.String:
                    var elementList = new List <IHoconElement>();
                    foreach (var element in clonedValue)
                    {
                        elementList.Add(element);
                    }
                    InsertRange(index, elementList);
                    break;
                }
            }

            switch (Parent)
            {
            case HoconField v:
                v.ResolveValue(this);
                break;

            case HoconArray a:
                a.ResolveValue(child);
                break;

            default:
                throw new Exception($"Invalid parent type while resolving substitution:{Parent.GetType()}");
            }
        }
Пример #4
0
        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);
        }
Пример #5
0
        internal void ResolveValue(HoconSubstitution sub)
        {
            if (sub.Type == HoconType.Empty)
            {
                Remove(sub);
                return;
            }

            if (sub.Type != HoconType.Array)
            {
                throw HoconParserException.Create(sub, sub.Path,
                                                  $"Substitution value must match the rest of the field type or empty. Parent value type: {Type}, substitution type: {sub.Type}");
            }
        }
Пример #6
0
        internal void ResolveValue(HoconSubstitution sub)
        {
            var subValue = (HoconValue)sub.Parent;

            if (sub.Type == HoconType.Empty)
            {
                subValue.Remove(sub);
                if (subValue.Count == 0)
                {
                    Remove(subValue);
                }
                return;
            }

            if (_arrayType != HoconType.Empty && sub.Type != _arrayType)
            {
                throw HoconParserException.Create(sub, sub.Path,
                                                  $"Substitution value must match the rest of the field type or empty. Array value type: {_arrayType}, substitution type: {sub.Type}");
            }
        }
Пример #7
0
        /// <summary>
        ///     Retrieves the next value token from the tokenizer and appends it
        ///     to the supplied element <paramref name="owner" />.
        /// </summary>
        /// <param name="owner">The element to append the next token.</param>
        /// <exception cref="System.Exception">End of file reached while trying to read a value</exception>
        private HoconValue ParseValue(IHoconElement owner)
        {
            // value is lazy initialized because we don't know what kind of value we're parsing
            HoconValue value   = null;
            var        parsing = true;

            while (parsing)
            {
                switch (_tokens.Current.Type)
                {
                case TokenType.Include:
                    var includeToken = _tokens.Current;
                    var includeValue = ParseInclude();
                    switch (includeValue.Type)
                    {
                    case HoconType.Empty:
                        value = new HoconEmptyValue(owner);
                        break;

                    case HoconType.Object:
                        value = GetHoconValueFromParentElement(owner, TokenType.StartOfObject);
                        value.ReParent(includeValue);
                        break;

                    case HoconType.Array:
                        value = GetHoconValueFromParentElement(owner, TokenType.StartOfArray);
                        value.ReParent(includeValue);
                        break;

                    default:
                        throw HoconParserException.Create(includeToken, Path,
                                                          "Include could never contain a literal type.");
                    }

                    break;

                case TokenType.LiteralValue:
                    // Consume leading whitespaces.
                    if (_tokens.Current.IsNonSignificant())
                    {
                        _tokens.ToNextSignificant();
                    }

                    if (_tokens.Current.Type != TokenType.LiteralValue)
                    {
                        break;
                    }

                    if (value == null)
                    {
                        value = GetHoconValueFromParentElement(owner, _tokens.Current.Type);
                    }

                    while (_tokens.Current.Type == TokenType.LiteralValue)
                    {
                        value.Add(HoconLiteral.Create(value, _tokens.Current));
                        _tokens.Next();
                    }

                    break;

                case TokenType.StartOfObject:
                    if (value == null)
                    {
                        value = GetHoconValueFromParentElement(owner, _tokens.Current.Type);
                    }

                    ParseObject(ref value);
                    break;

                case TokenType.StartOfArray:
                    if (value == null)
                    {
                        value = GetHoconValueFromParentElement(owner, _tokens.Current.Type);
                    }

                    // If this array is already initialized, we are going to overwrite it
                    if (value.Type == HoconType.Array && value.Count > 0)
                    {
                        value.Clear();
                    }

                    value.Add(ParseArray(value));
                    break;

                case TokenType.SubstituteOptional:
                case TokenType.SubstituteRequired:
                    if (value == null)
                    {
                        value = new HoconValue(owner);
                    }

                    var pointerPath = HoconPath.Parse(_tokens.Current.Value);
                    var sub         = new HoconSubstitution(value, pointerPath, _tokens.Current,
                                                            _tokens.Current.Type == TokenType.SubstituteRequired);
                    _substitutions.Add(sub);
                    _tokens.Next();
                    value.Add(sub);
                    break;

                case TokenType.PlusEqualAssign:
                    if (value == null)
                    {
                        value = new HoconValue(owner);
                    }

                    var subAssign = new HoconSubstitution(value, new HoconPath(Path), _tokens.Current, false);
                    _substitutions.Add(subAssign);
                    value.Add(subAssign);
                    value.Add(ParsePlusEqualAssignArray(value));
                    parsing = false;
                    break;

                case TokenType.EndOfObject:
                case TokenType.EndOfArray:
                    parsing = false;
                    break;

                case TokenType.Comment:
                    _tokens.ToNextSignificant();
                    break;

                case TokenType.EndOfLine:
                    parsing = false;
                    break;

                case TokenType.EndOfFile:
                case TokenType.Comma:
                    parsing = false;
                    break;

                case TokenType.Assign:
                    // Special case to support end of line after assign
                    _tokens.ToNextSignificantLine();
                    break;

                default:
                    throw HoconParserException.Create(_tokens.Current, Path,
                                                      $"Failed to parse Hocon value. Unexpected token: `{_tokens.Current.Type}`");
                }
            }

            if (value == null)
            {
                value = new HoconEmptyValue(owner);
            }

            // trim trailing whitespace if result is a literal
            if (value.Type.IsLiteral())
            {
                if (value[value.Count - 1] is HoconWhitespace)
                {
                    value.RemoveAt(value.Count - 1);
                }
            }
            return(value);
        }
Пример #8
0
        private bool IsValueCyclic(HoconField field, HoconSubstitution sub)
        {
            var pendingValues = new Stack <HoconValue>();
            var visitedFields = new List <HoconField> {
                field
            };
            var pendingSubs = new Stack <HoconSubstitution>();

            pendingSubs.Push(sub);

            while (pendingSubs.Count > 0)
            {
                var currentSub = pendingSubs.Pop();
                if (!_root.GetObject().TryGetField(currentSub.Path, out var currentField))
                {
                    continue;
                }

                if (visitedFields.Contains(currentField))
                {
                    return(true);
                }

                visitedFields.Add(currentField);
                pendingValues.Push(currentField.Value);
                while (pendingValues.Count > 0)
                {
                    var currentValue = pendingValues.Pop();

                    foreach (var value in currentValue)
                    {
                        switch (value)
                        {
                        case HoconLiteral _:
                            break;

                        case HoconObject o:
                            foreach (var f in o.Values)
                            {
                                if (visitedFields.Contains(f))
                                {
                                    return(true);
                                }

                                visitedFields.Add(f);
                                pendingValues.Push(f.Value);
                            }

                            break;

                        case HoconArray a:
                            foreach (var item in a.GetArray())
                            {
                                pendingValues.Push(item);
                            }
                            break;

                        case HoconSubstitution s:
                            pendingSubs.Push(s);
                            break;
                        }
                    }
                }
            }

            return(false);
        }
Пример #9
0
        /// <summary>
        /// Retrieves the next value token from the tokenizer and appends it
        /// to the supplied element <paramref name="owner"/>.
        /// </summary>
        /// <param name="owner">The element to append the next token.</param>
        /// <exception cref="System.Exception">End of file reached while trying to read a value</exception>
        public void ParseValue(HoconValue owner, string currentPath)
        {
            if (_reader.EoF)
            {
                throw new HoconParserException("End of file reached while trying to read a value");
            }

            _reader.PullWhitespaceAndComments();
            var start = _reader.Index;

            try
            {
                while (_reader.IsValue())
                {
                    Token t = _reader.PullValue();

                    switch (t.Type)
                    {
                    case TokenType.EoF:
                        break;

                    case TokenType.LiteralValue:
                        if (owner.IsObject())
                        {
                            //needed to allow for override objects
                            owner.Clear();
                        }
                        var lit = new HoconLiteral
                        {
                            Value = t.Value
                        };
                        owner.AppendValue(lit);

                        break;

                    case TokenType.ObjectStart:
                        ParseObject(owner, true, currentPath);
                        break;

                    case TokenType.ArrayStart:
                        HoconArray arr = ParseArray(currentPath);
                        owner.AppendValue(arr);
                        break;

                    case TokenType.Substitute:
                        HoconSubstitution sub = ParseSubstitution(t.Value);
                        _substitutions.Add(sub);
                        owner.AppendValue(sub);
                        break;
                    }
                    if (_reader.IsSpaceOrTab())
                    {
                        ParseTrailingWhitespace(owner);
                    }
                }

                IgnoreComma();
            }
            catch (HoconTokenizerException tokenizerException)
            {
                throw new HoconParserException(string.Format("{0}\r{1}", tokenizerException.Message, GetDiagnosticsStackTrace()), tokenizerException);
            }
            finally
            {
                //no value was found, tokenizer is still at the same position
                if (_reader.Index == start)
                {
                    throw new HoconParserException(string.Format("Hocon syntax error {0}\r{1}", _reader.GetHelpTextAtIndex(start), GetDiagnosticsStackTrace()));
                }
            }
        }
Пример #10
0
        /// <summary>
        /// Retrieves the next array token from the tokenizer.
        /// </summary>
        /// <returns>An array of elements retrieved from the token.</returns>
        private HoconArray ParseArray(IHoconElement owner)
        {
            var currentArray = new HoconArray(owner);

            // consume start of array token
            ConsumeWhitelines();

            IHoconElement lastValue = null;
            var           parsing   = true;

            while (parsing)
            {
                switch (_tokens.Current.Type)
                {
                case TokenType.Include:
                    if (lastValue != null)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Failed to parse Hocon array. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}, " +
                                                          $"found `{_tokens.Current.Type}` instead.");
                    }

                    lastValue = ParseInclude(currentArray);
                    break;

                case TokenType.StartOfArray:
                    if (lastValue != null)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Failed to parse Hocon array. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}, " +
                                                          $"found `{_tokens.Current.Type}` instead.");
                    }

                    // Array inside of arrays are parsed as values because it can be value concatenated with another array.
                    lastValue = ParseValue(currentArray);
                    break;

                case TokenType.StartOfObject:
                    if (lastValue != null)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Failed to parse Hocon array. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}, " +
                                                          $"found `{_tokens.Current.Type}` instead.");
                    }

                    lastValue = ParseObject(currentArray);
                    break;

                case TokenType.LiteralValue:
                    if (_tokens.Current.IsNonSignificant())
                    {
                        ConsumeWhitelines();
                    }
                    if (_tokens.Current.Type != TokenType.LiteralValue)
                    {
                        break;
                    }

                    if (lastValue != null)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Failed to parse Hocon array. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}, " +
                                                          $"found `{_tokens.Current.Type}` instead.");
                    }

                    lastValue = ParseValue(currentArray);
                    break;

                case TokenType.SubstituteOptional:
                case TokenType.SubstituteRequired:
                    if (lastValue != null)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Failed to parse Hocon array. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}, " +
                                                          $"found `{_tokens.Current.Type}` instead.");
                    }

                    var pointerPath       = HoconPath.Parse(_tokens.Current.Value);
                    HoconSubstitution sub = new HoconSubstitution(currentArray, pointerPath, _tokens.Current,
                                                                  _tokens.Current.Type == TokenType.SubstituteRequired);
                    _substitutions.Add(sub);
                    lastValue = sub;
                    _tokens.Next();
                    break;

                case TokenType.Comment:
                case TokenType.EndOfLine:
                    if (lastValue == null)
                    {
                        ConsumeWhitelines();
                        break;
                    }

                    switch (lastValue.Type)
                    {
                    case HoconType.Array:
                        currentArray.Add(lastValue);
                        break;

                    default:
                        currentArray.Add(lastValue);
                        break;
                    }
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.Comma:
                    if (lastValue == null)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Failed to parse Hocon array. Expected a valid value, found `{_tokens.Current.Type}` instead.");
                    }

                    switch (lastValue.Type)
                    {
                    case HoconType.Array:
                        currentArray.Add(lastValue);
                        break;

                    default:
                        currentArray.Add(lastValue);
                        break;
                    }
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.EndOfArray:
                    if (lastValue != null)
                    {
                        switch (lastValue.Type)
                        {
                        case HoconType.Array:
                            currentArray.Add(lastValue);
                            break;

                        default:
                            currentArray.Add(lastValue);
                            break;
                        }
                        lastValue = null;
                    }
                    parsing = false;
                    break;

                default:
                    throw HoconParserException.Create(_tokens.Current, Path,
                                                      $"Failed to parse Hocon array. Expected {TokenType.EndOfArray} but found {_tokens.Current.Type} instead.");
                }
            }

            // Consume end of array token
            _tokens.Next();
            return(currentArray);
        }
Пример #11
0
        /// <summary>
        /// Retrieves the next value token from the tokenizer and appends it
        /// to the supplied element <paramref name="owner"/>.
        /// </summary>
        /// <param name="owner">The element to append the next token.</param>
        /// <exception cref="System.Exception">End of file reached while trying to read a value</exception>
        private HoconValue ParseValue(IHoconElement owner)
        {
            var value   = new HoconValue(owner);
            var parsing = true;

            while (parsing)
            {
                switch (_tokens.Current.Type)
                {
                case TokenType.Include:
                    value.Add(ParseInclude(value));
                    break;

                case TokenType.LiteralValue:
                    // Consume leading whitespaces.
                    if (_tokens.Current.IsNonSignificant())
                    {
                        ConsumeWhitespace();
                    }
                    if (_tokens.Current.Type != TokenType.LiteralValue)
                    {
                        break;
                    }

                    while (_tokens.Current.Type == TokenType.LiteralValue)
                    {
                        value.Add(HoconLiteral.Create(value, _tokens.Current));
                        _tokens.Next();
                    }
                    break;

                case TokenType.StartOfObject:
                    value.Add(ParseObject(value));
                    break;

                case TokenType.StartOfArray:
                    value.Add(ParseArray(value));
                    break;

                case TokenType.SubstituteOptional:
                case TokenType.SubstituteRequired:
                    var pointerPath       = HoconPath.Parse(_tokens.Current.Value);
                    HoconSubstitution sub = new HoconSubstitution(value, pointerPath, _tokens.Current,
                                                                  _tokens.Current.Type == TokenType.SubstituteRequired);
                    _substitutions.Add(sub);
                    value.Add(sub);
                    _tokens.Next();
                    break;

                case TokenType.EndOfObject:
                case TokenType.EndOfArray:
                    parsing = false;
                    break;

                // comments automatically stop value parsing.
                case TokenType.Comment:
                    ConsumeWhitelines();
                    parsing = false;
                    break;

                case TokenType.EndOfLine:
                    parsing = false;
                    break;

                case TokenType.EndOfFile:
                case TokenType.Comma:
                    parsing = false;
                    break;

                case TokenType.PlusEqualAssign:
                    HoconSubstitution subAssign = new HoconSubstitution(value, new HoconPath(Path), _tokens.Current, false);
                    _substitutions.Add(subAssign);
                    value.Add(subAssign);

                    value.Add(ParsePlusEqualAssignArray(value));
                    parsing = false;
                    break;

                case TokenType.Assign:
                    ConsumeWhitelines();
                    break;

                default:
                    throw HoconParserException.Create(_tokens.Current, Path,
                                                      $"Failed to parse Hocon value. Unexpected token: `{_tokens.Current.Type}`");
                }
            }

            // trim trailing whitespace if result is a literal
            if (value.Type == HoconType.Literal)
            {
                if (value[value.Count - 1] is HoconWhitespace)
                {
                    value.RemoveAt(value.Count - 1);
                }
            }
            return(value);
        }