Beispiel #1
0
        // parse path value
        private HoconPath ParseKey()
        {
            // sanity check
            if (_tokens.Current.IsNonSignificant() || _tokens.Current.Type != TokenType.LiteralValue)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Internal parser error, ParseKey() is called on an invalid token. Should be `{TokenType.LiteralValue}`, found `{_tokens.Current.Type}` instead.");
            }

            var keyTokens = new HoconTokenizerResult();

            while (_tokens.Current.Type == TokenType.LiteralValue)
            {
                keyTokens.Add(_tokens.Current);
                _tokens.Next();
            }

            // TODO: this is janky, fix this
            keyTokens.Reverse();
            while (keyTokens.Count > 0 && keyTokens[0].LiteralType == TokenLiteralType.Whitespace)
            {
                keyTokens.RemoveAt(0);
            }
            keyTokens.Reverse();

            keyTokens.Add(new Token("", TokenType.EndOfFile, null));

            return(HoconPath.FromTokens(keyTokens));
        }
Beispiel #2
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()}");
            }
        }
Beispiel #3
0
        private HoconRoot ParseText(string text, bool resolveSubstitutions, HoconIncludeCallbackAsync includeCallback)
        {
            if (string.IsNullOrWhiteSpace(text))
            {
                throw new HoconParserException(
                          $"Parameter {nameof(text)} is null or empty.\n" +
                          "If you want to create an empty Hocon HoconRoot, use \"{}\" instead.");
            }

            if (includeCallback != null)
            {
                _includeCallback = includeCallback;
            }

            try
            {
                _tokens = new HoconTokenizer(text).Tokenize();
                _root   = new HoconValue(null);
                ParseTokens();
                if (resolveSubstitutions)
                {
                    ResolveSubstitutions();
                }
            }
            catch (HoconTokenizerException e)
            {
                throw HoconParserException.Create(e, null, $"Error while tokenizing Hocon: {e.Message}", e);
            }
            catch (HoconException e)
            {
                throw HoconParserException.Create(_tokens.Current, Path, e.Message, e);
            }

            return(new HoconRoot(_root, _substitutions));
        }
Beispiel #4
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);
        }
Beispiel #5
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()}");
            }
        }
Beispiel #6
0
        private void ResolveSubstitutions()
        {
            foreach (var sub in _substitutions)
            {
                // Retrieve value
                HoconValue res;
                try
                {
                    res = ResolveSubstitution(sub);
                }
                catch (HoconException e)
                {
                    throw HoconParserException.Create(sub, sub.Path, $"Invalid substitution declaration. {e.Message}.", e);
                }

                if (res != null)
                {
                    sub.ResolvedValue = res;
                    continue;
                }

                // Try to pull value from environment
                string envValue = null;
                try
                {
                    envValue = Environment.GetEnvironmentVariable(sub.Path.Value);
                }
                catch (Exception)
                {
                    // ignored
                }

                if (envValue != null)
                {
                    // undefined value resolved to an environment variable
                    res = new HoconValue(sub.Parent);
                    if (envValue.NeedQuotes())
                    {
                        res.Add(new HoconQuotedString(sub.Parent, envValue));
                    }
                    else
                    {
                        res.Add(new HoconUnquotedString(sub.Parent, envValue));
                    }

                    sub.ResolvedValue = res;
                    continue;
                }

                // ${ throws exception if it is not resolved
                if (sub.Required)
                {
                    throw HoconParserException.Create(sub, sub.Path, $"Unresolved substitution: {sub.Path}");
                }

                sub.ResolvedValue = new HoconEmptyValue(sub.Parent);
            }
        }
Beispiel #7
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}");
            }
        }
Beispiel #8
0
        private HoconField ParseField(HoconObject owner)
        {
            // sanity check
            if (_tokens.Current.Type != TokenType.LiteralValue)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Failed to parse Hocon field. Expected start of field {TokenType.LiteralValue}, " +
                                                  $"found {_tokens.Current.Type} instead.");
            }

            var pathDelta = ParseKey();

            if (_tokens.Current.IsNonSignificant())
            {
                ConsumeWhitelines();
            }

            // sanity check
            if (_tokens.Current.Type != TokenType.Assign &&
                _tokens.Current.Type != TokenType.StartOfObject &&
                _tokens.Current.Type != TokenType.PlusEqualAssign)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Failed to parse Hocon field. Expected {TokenType.Assign}, {TokenType.StartOfObject} " +
                                                  $"or {TokenType.PlusEqualAssign}, found {{_tokens.Current.Type}} instead.");
            }

            // sanity check
            if (pathDelta == null || pathDelta.Count == 0)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  "Failed to parse Hocon field. ParseField() was called with null or empty path");
            }

            List <HoconField> childInPath = owner.TraversePath(pathDelta);

            Path.AddRange(pathDelta);
            HoconField currentField = childInPath[childInPath.Count - 1];

            var parsedValue = ParseValue(currentField);

            foreach (var removedSub in currentField.SetValue(parsedValue))
            {
                _substitutions.Remove(removedSub);
            }

            Path.RemoveRange(Path.Count - pathDelta.Count, pathDelta.Count);
            return(childInPath[0]);
        }
Beispiel #9
0
        internal static HoconPath FromTokens(HoconTokenizerResult tokens)
        {
            if (tokens == null)
            {
                throw new ArgumentNullException(nameof(tokens));
            }

            var result = new List <string>();
            var sb     = new StringBuilder();

            while (tokens.Current.Type == TokenType.LiteralValue)
            {
                switch (tokens.Current.LiteralType)
                {
                case TokenLiteralType.TripleQuotedLiteralValue:
                    throw HoconParserException.Create(tokens.Current, null,
                                                      "Triple quoted string could not be used in path expression.");

                case TokenLiteralType.QuotedLiteralValue:
                    // Normalize quoted keys, remove the quotes if the key doesn't need them.
                    //sb.Append(tokens.Current.Value.NeedQuotes() ? $"\"{tokens.Current.Value}\"" : tokens.Current.Value);
                    sb.Append(tokens.Current.Value);
                    break;

                default:
                    var split = tokens.Current.Value.Split('.');
                    for (var i = 0; i < split.Length - 1; ++i)
                    {
                        sb.Append(split[i]);
                        result.Add(sb.ToString());
                        sb.Clear();
                    }

                    sb.Append(split[split.Length - 1]);
                    break;
                }

                tokens.Next();
            }

            result.Add(sb.ToString());
            return(new HoconPath(result));
        }
Beispiel #10
0
        private void ParseField(HoconObject owner)
        {
            // sanity check
            if (_tokens.Current.IsNonSignificant() || _tokens.Current.Type != TokenType.LiteralValue)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Failed to parse Hocon field. Expected start of field {TokenType.LiteralValue}, " +
                                                  $"found {_tokens.Current.Type} instead.");
            }

            var relativePath = ParseKey();

            if (_tokens.Current.Type == TokenType.EndOfLine)
            {
                _tokens.ToNextSignificantLine();
            }

            // sanity check
            if (_tokens.Current.Type != TokenType.Assign &&
                _tokens.Current.Type != TokenType.StartOfObject &&
                _tokens.Current.Type != TokenType.PlusEqualAssign)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Failed to parse Hocon field. Expected {TokenType.Assign}, {TokenType.StartOfObject} " +
                                                  $"or {TokenType.PlusEqualAssign}, found {_tokens.Current.Type} instead.");
            }

            // sanity check
            if (relativePath == null || relativePath.Count == 0)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  "Failed to parse Hocon field. Null or empty path");
            }

            Path.AddRange(relativePath);

            var currentField = owner.TraversePath(relativePath);

            currentField.SetValue(ParseValue(currentField));

            Path.RemoveRange(Path.Count - relativePath.Count, relativePath.Count);
        }
Beispiel #11
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}");
            }
        }
Beispiel #12
0
        internal void ResolveValue(IHoconElement child)
        {
            if (child.Type == HoconType.Empty)
            {
                Remove(child);
            }
            else if (Type == HoconType.Empty)
            {
                Type = child.Type;
            }
            else if (Type != child.Type)
            {
                var sub = (HoconSubstitution)child;
                throw HoconParserException.Create(sub, sub.Path,
                                                  "Invalid substitution, substituted type must match its sibling type. " +
                                                  $"Sibling type:{Type}, substitution type:{child.Type}");
            }

            ((HoconField)Parent).ResolveValue(this);
        }
Beispiel #13
0
        private void ParseTokens()
        {
            if (_tokens.Current.IsNonSignificant() || _tokens.Current.Type == TokenType.EndOfLine)
            {
                _tokens.ToNextSignificantLine();
            }

            // Array Hocon files are handled differently than normal Hocon file.
            // Array Hocon files HAVE to start with an angle bracket and can only contain ONE array
            if (_tokens.Current.Type == TokenType.StartOfArray)
            {
                _root.Add(ParseArray(_root));

                // Sanity check
                if (_tokens.Current.IsNonSignificant() || _tokens.Current.Type == TokenType.EndOfLine)
                {
                    _tokens.ToNextSignificantLine();
                }

                if (_tokens.Current.Type != TokenType.EndOfFile)
                {
                    throw HoconParserException.Create(_tokens.Current, null,
                                                      $"Illegal token type: {_tokens.Current.Type}. Hocon array file can only contain one array.",
                                                      null);
                }

                return;
            }

            if (_tokens.Current.Type != TokenType.StartOfObject)
            {
                // This is a "headless" Hocon file, we'll normalize the file
                // by inserting the proper open and close curly brackets
                _tokens.Insert(new Token("{", TokenType.StartOfObject, null));
                _tokens.Insert(_tokens.Count - 1, new Token("}", TokenType.EndOfObject, null));
            }

            ParseObject(ref _root);
        }
Beispiel #14
0
        private HoconValue GetHoconValueFromParentElement(IHoconElement parentElement, TokenType type)
        {
            if (parentElement is HoconArray arr)
            {
                return(new HoconValue(arr));
            }

            if (!(parentElement is HoconField hf))
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  "Invalid parent element for HoconValue. Valid objects are HoconArray and HoconField.");
            }

            var fieldValue = hf.Value;

            if (fieldValue == null)
            {
                return(new HoconValue(hf));
            }

            switch (type)
            {
            case TokenType.LiteralValue:
                return(new HoconValue(hf));

            case TokenType.StartOfObject when fieldValue.Type == HoconType.Object:
                return(fieldValue);

            case TokenType.StartOfArray when fieldValue.Type == HoconType.Array:
                return(fieldValue);

            case TokenType.SubstituteOptional:
            case TokenType.SubstituteRequired:
            case TokenType.Include:
            default:
                return(new HoconValue(hf));
            }
        }
Beispiel #15
0
        private HoconValue ParseInclude()
        {
            // Sanity check
            if (_tokens.Current.Type != TokenType.Include)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Internal parser error, ParseInclude() is called on an invalid token: `{_tokens.Current.Type}`");
            }

            var    parenthesisCount = 0;
            var    required         = false;
            var    callbackType     = HoconCallbackType.File;
            string fileName         = null;
            var    includeToken     = _tokens.Current;

            var expectedTokens = new List <TokenType>(new[]
            {
                TokenType.Required,
                TokenType.Url,
                TokenType.File,
                TokenType.Classpath,
                TokenType.LiteralValue,
                TokenType.ParenthesisEnd,
                TokenType.EndOfLine
            });

            var parsing = true;

            while (parsing)
            {
                _tokens.ToNextSignificant();
                var currentType = _tokens.Current.Type;
                if (expectedTokens.All(t => t != currentType))
                {
                    throw HoconParserException.Create(_tokens.Current, Path,
                                                      $"Invalid token in include: `{currentType}`", null);
                }

                switch (currentType)
                {
                case TokenType.ParenthesisEnd:
                    if (parenthesisCount <= 0)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          "Unexpected closing parenthesis.", null);
                    }

                    parenthesisCount--;
                    parsing = parenthesisCount > 0;
                    break;

                case TokenType.Required:
                    _tokens.ToNextSignificant();
                    // The next token after the "required" keyword have to be an open paren
                    if (_tokens.Current.Type != TokenType.ParenthesisStart)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Expected {TokenType.ParenthesisStart}, found `{_tokens.Current.Type}` instead.");
                    }

                    parenthesisCount++;
                    required = true;
                    expectedTokens.Remove(TokenType.Required);
                    break;

                case TokenType.Url:
                    _tokens.ToNextSignificant();
                    // The next token after the "url" keyword have to be an open paren
                    if (_tokens.Current.Type != TokenType.ParenthesisStart)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Expected {TokenType.ParenthesisStart}, found `{_tokens.Current.Type}` instead.");
                    }

                    parenthesisCount++;
                    callbackType = HoconCallbackType.Url;
                    expectedTokens.Remove(TokenType.Required);
                    expectedTokens.Remove(TokenType.Url);
                    expectedTokens.Remove(TokenType.File);
                    expectedTokens.Remove(TokenType.Classpath);
                    break;

                case TokenType.File:
                    _tokens.ToNextSignificant();
                    // The next token after the "file" keyword have to be an open paren
                    if (_tokens.Current.Type != TokenType.ParenthesisStart)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Expected {TokenType.ParenthesisStart}, found `{_tokens.Current.Type}` instead.");
                    }

                    parenthesisCount++;
                    callbackType = HoconCallbackType.File;
                    expectedTokens.Remove(TokenType.Required);
                    expectedTokens.Remove(TokenType.Url);
                    expectedTokens.Remove(TokenType.File);
                    expectedTokens.Remove(TokenType.Classpath);
                    break;

                case TokenType.Classpath:
                    _tokens.ToNextSignificant();
                    // The next token after the "classpath" keyword have to be an open paren
                    if (_tokens.Current.Type != TokenType.ParenthesisStart)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Expected {TokenType.ParenthesisStart}, found `{_tokens.Current.Type}` instead.");
                    }

                    parenthesisCount++;
                    callbackType = HoconCallbackType.Resource;
                    expectedTokens.Remove(TokenType.Required);
                    expectedTokens.Remove(TokenType.Url);
                    expectedTokens.Remove(TokenType.File);
                    expectedTokens.Remove(TokenType.Classpath);
                    break;

                case TokenType.LiteralValue:
                    if (_tokens.Current.IsNonSignificant())
                    {
                        _tokens.ToNextSignificant();
                    }

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

                    if (_tokens.Current.LiteralType != TokenLiteralType.QuotedLiteralValue)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Invalid literal type for declaring file name. Expected {TokenLiteralType.QuotedLiteralValue}, " +
                                                          $"found `{_tokens.Current.LiteralType}` instead.");
                    }

                    fileName = _tokens.Current.Value;
                    expectedTokens.Remove(TokenType.LiteralValue);
                    expectedTokens.Remove(TokenType.Required);
                    expectedTokens.Remove(TokenType.Url);
                    expectedTokens.Remove(TokenType.File);
                    expectedTokens.Remove(TokenType.Classpath);

                    parsing = parenthesisCount > 0;
                    break;

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

            if (parenthesisCount > 0)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Expected {TokenType.ParenthesisEnd}, found `{_tokens.Current.Type}`");
            }

            if (fileName == null)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  "Include does not contain any quoted file name value.");
            }

            // Consume the last token
            _tokens.ToNextSignificant();

            var includeHocon = _includeCallback(callbackType, fileName).ConfigureAwait(false).GetAwaiter().GetResult();

            if (string.IsNullOrWhiteSpace(includeHocon))
            {
                if (required)
                {
                    throw HoconParserException.Create(includeToken, Path,
                                                      "Invalid Hocon include. Include was declared as required but include callback returned a null or empty string.");
                }
                return(new HoconEmptyValue(null));
            }

            var includeRoot = new HoconParser().ParseText(includeHocon, false, _includeCallback);

            /*
             * if (owner != null && owner.Type != HoconType.Empty && owner.Type != includeRoot.Value.Type)
             *  throw HoconParserException.Create(includeToken, Path,
             *      "Invalid Hocon include. Hocon config substitution type must be the same as the field it's merged into. " +
             *      $"Expected type: `{owner.Type}`, type returned by include callback: `{includeRoot.Value.Type}`");
             */

            // fixup the substitution, add the current path as a prefix to the substitution path
            foreach (var substitution in includeRoot.Substitutions)
            {
                substitution.Path.InsertRange(0, Path);
            }
            _substitutions.AddRange(includeRoot.Substitutions);

            // re-parent the value returned by the callback to the owner of the include declaration
            return(includeRoot.Value);
        }
Beispiel #16
0
        // The owner in this context can be either an object or an array.
        private HoconObject ParseObject(IHoconElement owner)
        {
            var hoconObject = new HoconObject(owner);

            if (_tokens.Current.Type != TokenType.StartOfObject &&
                _tokens.Current.Type != TokenType.LiteralValue &&
                _tokens.Current.Type != TokenType.Include)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  $"Failed to parse Hocon object. Expected `{TokenType.StartOfObject}` or `{TokenType.LiteralValue}, " +
                                                  $"found `{_tokens.Current.Type}` instead.");
            }

            var headless = true;

            if (_tokens.Current.Type == TokenType.StartOfObject)
            {
                headless = false;
                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 object. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}`, " +
                                                          $"found `{_tokens.Current.Type}` instead.");
                    }

                    lastValue = ParseInclude(hoconObject);
                    break;

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

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

                    lastValue = ParseField(hoconObject);
                    break;

                // TODO: can an object be declared floating without being assigned to a field?
                //case TokenType.StartOfObject:
                case TokenType.Comment:
                case TokenType.EndOfLine:
                    switch (lastValue)
                    {
                    case null:
                        ConsumeWhitelines();
                        break;

                    case HoconField _:
                        break;

                    default:
                        ((HoconValue)hoconObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

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

                    case HoconField _:
                        break;

                    default:
                        ((HoconValue)hoconObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.EndOfObject:
                case TokenType.EndOfFile:
                    if (headless && _tokens.Current.Type != TokenType.EndOfFile)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Failed to parse Hocon object. Expected {TokenType.EndOfFile} but found {_tokens.Current.Type} instead.");
                    }

                    if (!headless && _tokens.Current.Type != TokenType.EndOfObject)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path,
                                                          $"Failed to parse Hocon object. Expected {TokenType.EndOfObject} but found {_tokens.Current.Type} instead.");
                    }

                    switch (lastValue)
                    {
                    case null:
                        break;

                    case HoconField _:
                        break;

                    default:
                        ((HoconValue)hoconObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    parsing   = false;
                    break;

                default:
                    throw HoconParserException.Create(_tokens.Current, Path,
                                                      $"Failed to parse Hocon object. Unexpected token `{_tokens.Current.Type}`.");
                }
            }
            if (_tokens.Current.Type == TokenType.EndOfObject)
            {
                _tokens.Next();
            }
            return(hoconObject);
        }
Beispiel #17
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)
        {
            // sanity check
            if (_tokens.Current.Type != TokenType.StartOfArray)
            {
                throw HoconParserException.Create(_tokens.Current, Path,
                                                  "Failed to parse Hocon array. " +
                                                  $"Expected {TokenType.StartOfArray}, found {_tokens.Current.Type} instead.");
            }

            var currentArray = new HoconArray(owner);

            // consume start of array token
            _tokens.ToNextSignificant();

            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 array. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}, " +
                                                          $"found `{_tokens.Current.Type}` instead.");
                    }

                    currentArray.Add(ParseValue(currentArray));
                    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 array. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}, " +
                                                          $"found `{_tokens.Current.Type}` instead.");
                    }

                    currentArray.Add(ParseValue(currentArray));
                    valueWasParsed = true;
                    break;

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

                    currentArray.Add(ParseValue(currentArray));
                    valueWasParsed = true;
                    break;

                case TokenType.StartOfArray:
                    if (valueWasParsed)
                    {
                        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.
                    currentArray.Add(ParseValue(currentArray));
                    valueWasParsed = true;
                    break;

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

                    currentArray.Add(ParseValue(currentArray));
                    valueWasParsed = true;
                    break;

                case TokenType.EndOfArray:
                    valueWasParsed = false;

                    // If there is a next array on the same like - let's move to it's first element
                    if (_tokens.ForwardMatch(TokenType.StartOfArray))
                    {
                        _tokens.ToNextSignificant();
                        _tokens.Next();
                    }
                    else
                    {
                        // Otherwise, array value is fully loaded and nothing to do here
                        parsing = false;
                    }

                    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 array. Expected a valid value, found `{_tokens.Current.Type}` instead.");
                    }

                    valueWasParsed = false;
                    _tokens.ToNextSignificant();
                    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);
        }
Beispiel #18
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
            _tokens.ToNextSignificant();

            switch (_tokens.Current.Type)
            {
            case TokenType.Include:
                var includeToken = _tokens.Current;
                var includeValue = ParseInclude();
                if (includeValue.Type == HoconType.Empty)
                {
                    break;
                }

                if (currentArray.Type != HoconType.Empty && currentArray.Type != includeValue.Type)
                {
                    throw HoconParserException.Create(includeToken, Path,
                                                      "Invalid Hocon include. Hocon config substitution type must be the same as the field it's merged into. " +
                                                      $"Expected type: `{currentArray.Type}`, type returned by include callback: `{includeValue.Type}`");
                }

                currentArray.Add((HoconValue)includeValue.Clone(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(ParseValue(currentArray));
                break;

            case TokenType.LiteralValue:
                if (_tokens.Current.IsNonSignificant())
                {
                    _tokens.ToNextSignificant();
                }

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

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

            case TokenType.SubstituteOptional:
            case TokenType.SubstituteRequired:
                currentArray.Add(ParseValue(currentArray));
                break;

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

            return(currentArray);
        }
Beispiel #19
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);
        }
Beispiel #20
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);
        }
Beispiel #21
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);
        }
Beispiel #22
0
        // 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();
        }
Beispiel #23
0
        private void ParseTokens()
        {
            if (_tokens.Current.IsNonSignificant())
            {
                ConsumeWhitelines();
            }

            while (_tokens.Current.Type != TokenType.EndOfFile)
            {
                switch (_tokens.Current.Type)
                {
                case TokenType.Include:
                    var parsedInclude = ParseInclude(null);
                    if (_root.Type != HoconType.Object)
                    {
                        _root.Clear();
                        _root.Add(parsedInclude.GetObject());
                    }
                    else
                    {
                        _root.Add(parsedInclude.GetObject());
                    }
                    break;

                // Hocon config file may contain one array and one array only
                case TokenType.StartOfArray:
                    _root.Clear();
                    _root.Add(ParseArray(null));
                    ConsumeWhitelines();
                    if (_tokens.Current.Type != TokenType.EndOfFile)
                    {
                        throw HoconParserException.Create(_tokens.Current, Path, "Hocon config can only contain one array or one object.");
                    }
                    return;

                case TokenType.StartOfObject:
                {
                    var parsedObject = ParseObject(null);
                    if (_root.Type != HoconType.Object)
                    {
                        _root.Clear();
                        _root.Add(parsedObject);
                    }
                    else
                    {
                        _root.Add(parsedObject.GetObject());
                    }
                    break;
                }

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

                    var parsedObject = ParseObject(null);
                    if (_root.Type != HoconType.Object)
                    {
                        _root.Clear();
                        _root.Add(parsedObject);
                    }
                    else
                    {
                        _root.Add(parsedObject.GetObject());
                    }
                    break;
                }

                case TokenType.Comment:
                case TokenType.EndOfLine:
                case TokenType.EndOfFile:
                case TokenType.EndOfObject:
                case TokenType.EndOfArray:
                    _tokens.Next();
                    break;

                default:
                    throw HoconParserException.Create(_tokens.Current, null, $"Illegal token type: {_tokens.Current.Type}", null);
                }
            }
        }