예제 #1
0
        public static IAtom[] Parse(IEnumerable <LexToken> tokens, PenisConfiguration config)
        {
            var atoms = ParseInner(tokens, config);

            CheckSyntax(atoms);

            return(atoms.ToArray());
        }
예제 #2
0
        /// <summary>
        /// Deserializes from a PENIS-serialized string into an object of type <paramref name="type"/>.
        /// </summary>
        /// <param name="type">The object's type.</param>
        /// <param name="str">The PENIS-serialized object text.</param>
        /// <param name="config">The configuration to use.</param>
        public static object DeserializeObject(Type type, string str, PenisConfiguration config = null)
        {
            config = config ?? PenisConfiguration.Default;

            var lex    = Lexer.Lex(str.SplitLines(), config);
            var parsed = Parser.Parse(lex, config);

            return(DeserializeObject(parsed, type));
        }
예제 #3
0
        public static string Unlex(IEnumerable <LexToken> tokens, PenisConfiguration config)
        {
            var str = new StringBuilder();

            foreach (var token in tokens)
            {
                if (token.FirstInLine)
                {
                    if (str.Length != 0 || token.Type == LexToken.Types.EmptyLine)
                    {
                        str.AppendLine();
                    }

                    str.Append(new string(IndentationChar, config.IndentationCount * token.IndentLevel));
                }

                switch (token.Type)
                {
                case LexToken.Types.Key:
                    str.Append(token.Value + KeyValueSeparator);
                    break;

                case LexToken.Types.BeginMultilineString:
                    str.Append(" " + BeginMultilineString);
                    break;

                case LexToken.Types.EndMultilineString:
                    str.Append(EndMultilineString);
                    break;

                case LexToken.Types.MultilineText:
                    str.Append(token.Value);
                    break;

                case LexToken.Types.Value:
                    str.Append(" " + token.Value);
                    break;

                case LexToken.Types.Comment:
                    if (!token.FirstInLine)
                    {
                        str.Append(" ");
                    }

                    str.Append(CommentBegin + token.Value);
                    break;

                case LexToken.Types.BeginListItem:
                    str.Append(ListItemBegin);
                    break;
                }
            }

            return(str.ToString());
        }
예제 #4
0
        public static IEnumerable <LexToken> Lex(string[] lines, PenisConfiguration config)
        {
            int i     = 0;
            var state = new State();

            foreach (var line in lines)
            {
                foreach (var tk in ParseLine(line, i++, state, config))
                {
                    yield return(tk);
                }
            }
        }
예제 #5
0
        internal static object ConvertAtom(IAtom atom, Type targetType, PenisConfiguration config)
        {
            if (atom is KeyValueAtom kva)
            {
                if (targetType.IsEnum && kva.Value is string str)
                {
                    return(Enum.Parse(targetType, str, config.IgnoreCase));
                }

                return(kva.Value);
            }
            else if (atom is ContainerAtom container)
            {
                if (container.IsList)
                {
                    if (!targetType.IsArray)
                    {
                        throw new ConvertException("Found list in file but target field isn't an array.");
                    }

                    var itemType = targetType.GetElementType();
                    var items    = container.Atoms.Select(o => ConvertAtom(o, itemType, config));

                    var array = (Array)Activator.CreateInstance(targetType, new object[] { container.Atoms.Count });

                    Array.Copy(items.ToArray(), array, array.Length);

                    return(array);
                }
                else
                {
                    return(DeserializeObject(container.Atoms, targetType));
                }
            }

            throw new ParserException("Invalid atom found.");
        }
예제 #6
0
 /// <summary>
 /// Deserializes from a PENIS-serialized string into an object of type <typeparamref name="T"/>.
 /// </summary>
 /// <typeparam name="T">The object's type.</typeparam>
 /// <param name="str">The PENIS-serialized object text.</param>
 /// <param name="config">The configuration to use.</param>
 public static T DeserializeObject <T>(string str, PenisConfiguration config = null)
 {
     return((T)DeserializeObject(typeof(T), str, config));
 }
예제 #7
0
 /// <summary>
 /// Serializes <paramref name="obj"/> into a PENIS file using <paramref name="config"/>.
 /// </summary>
 /// <param name="obj">The object to be serialized.</param>
 /// <param name="config">The configuration to use.</param>
 public static string SerializeObject(object obj, PenisConfiguration config = null)
 {
     return(Lexer.Unlex(Parser.Unparse(GetAtoms(obj).NotNull()), config ?? PenisConfiguration.Default));
 }
예제 #8
0
        internal static object DeserializeObject(IEnumerable <IAtom> atoms, Type type, PenisConfiguration config = null)
        {
            config = config ?? PenisConfiguration.Default;

            var instance  = Activator.CreateInstance(type);
            var atomsList = new List <IAtom>(atoms);

            foreach (var member in GetPropsAndFields(type))
            {
                var atom = atomsList.SingleOrDefault(o => o.Key?.Equals(member.Name,
                                                                        config.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) ?? false);

                if (atom != null)
                {
                    atomsList.Remove(atom);

                    member.SetValue(instance, ConvertAtom(atom, member.GetMemberType(), config));
                }
            }

            return(instance);
        }
예제 #9
0
        private static IEnumerable <IAtom> ParseInner(IEnumerable <LexToken> tokens, PenisConfiguration config)
        {
            var linkedTokens = new LinkedList <LexToken>(tokens);
            LinkedListNode <LexToken> tokenNode = linkedTokens.First;

            var   containerStack     = new Stack <(LexToken Token, ContainerAtom Atom)>();
            IAtom lastAtom           = null;
            var   pendingDecorations = new List <IDecoration>();

            do
            {
                if (tokenNode == null)
                {
                    break;
                }

                var token = tokenNode.Value;

                //Check if we exited any containers
                while (containerStack.Count > 0 && token.IndentLevel <= containerStack.Peek().Token.IndentLevel)
                {
                    var atom = containerStack.Pop().Atom;

                    if (containerStack.Count > 0)
                    {
                        containerStack.Peek().Atom.Atoms.Add(atom);
                    }
                    else
                    {
                        yield return(lastAtom = atom);
                    }
                }

                if (token.Type == LexToken.Types.Key || token.Type == LexToken.Types.BeginListItem)
                {
                    var nextType = tokenNode.Next?.Value.Type;

                    //Check for simple "key: value" pair or list item
                    if (nextType == LexToken.Types.Value || nextType == LexToken.Types.BeginMultilineString)
                    {
                        object value;

                        if (nextType == LexToken.Types.BeginMultilineString)
                        {
                            var startingToken = tokenNode;
                            tokenNode = tokenNode.Next;

                            string str = "";

                            while ((tokenNode = tokenNode.Next).Value.Type != LexToken.Types.EndMultilineString)
                            {
                                str += tokenNode.Value.Value + "\n";
                            }

                            value     = str.TrimEnd('\n');
                            tokenNode = startingToken;
                        }
                        else
                        {
                            value = ParseValue(tokenNode.Next.Value.Value);
                        }

                        var atom = new KeyValueAtom(token.Value, value);

                        if (containerStack.Count > 0)
                        {
                            containerStack.Peek().Atom.Atoms.Add(lastAtom = atom);
                        }
                        else
                        {
                            yield return(lastAtom = atom);
                        }

                        tokenNode = tokenNode.Next;
                    }
                    else //Item container (i.e. object or list) begins
                    {
                        bool isList = tokenNode != linkedTokens.Last && tokenNode.Next.Value.Type == LexToken.Types.BeginListItem;

                        var atom = new ContainerAtom(token.Value, isList);
                        lastAtom = atom;

                        containerStack.Push((token, atom));
                    }
                }
                else if (token.Type == LexToken.Types.EmptyLine)
                {
                    if (lastAtom != null)
                    {
                        lastAtom.Decorations.Add(new EmptyLineDecoration(EmptyLineDecoration.Positions.After));
                    }
                    else
                    {
                        pendingDecorations.Add(new EmptyLineDecoration(EmptyLineDecoration.Positions.Before));
                    }

                    continue;
                }
                else if (token.Type == LexToken.Types.Comment)
                {
                    if (lastAtom != null)
                    {
                        lastAtom.Decorations.Add(new CommentDecoration(token.Value,
                                                                       token.FirstInLine ? CommentDecoration.Positions.After : CommentDecoration.Positions.Inline));
                    }
                    else
                    {
                        pendingDecorations.Add(new CommentDecoration(token.Value, CommentDecoration.Positions.Before));
                    }

                    continue;
                }

                foreach (var item in pendingDecorations)
                {
                    lastAtom.Decorations.Add(item);
                }

                pendingDecorations.Clear();
            } while ((tokenNode = tokenNode.Next) != null);

            if (containerStack.Count != 0)
            {
                (LexToken Token, IAtom Atom)container = default;

                do
                {
                    if (containerStack.Count > 0)
                    {
                        container = containerStack.Pop();
                    }

                    if (containerStack.Count == 0)
                    {
                        yield return(container.Atom);
                    }
                    else
                    {
                        containerStack.Peek().Atom.Atoms.Add(container.Atom);
                    }
                } while (containerStack.Count > 0);
            }
        }
예제 #10
0
        private static IEnumerable <LexToken> ParseLine(string line, int lineIndex, State state, PenisConfiguration config)
        {
            if (string.IsNullOrWhiteSpace(line) && !state.InMultiline)
            {
                yield return(new LexToken(LexToken.Types.EmptyLine, null, lineIndex, 0, 0, 0, true));

                yield break;
            }

            int indentation = line.TakeWhile(o => o == IndentationChar).Count() / config.IndentationCount;
            //line = line.Substring(indentation * IndentationCount);

            bool onIndentation = true;
            bool hasAddedAny   = false;

            if (state.InMultiline)
            {
                if (line.Trim() == EndMultilineString)
                {
                    state.InMultiline = false;
                    yield return(new LexToken(LexToken.Types.EndMultilineString, null, lineIndex, 0, line.Length - 1, 0, IsFirst()));
                }
                else
                {
                    //If line begins and ends with quotes, preserve spaces
                    if (line.Length > 1 && line[0] == OpenString && line[line.Length - 1] == CloseString)
                    {
                        line = line.TrimStart(OpenString).TrimEnd(CloseString);
                    }
                    else
                    {
                        line = line.Trim();
                    }

                    yield return(new LexToken(LexToken.Types.MultilineText, line, lineIndex, 0, line.Length - 1, 0, IsFirst()));
                }

                yield break;
            }

            for (int i = 0; i < line.Length; i++)
            {
                if (line[i] == CommentBegin && (i == 0 || (i > 0 && line[i - 1] != EscapeCharacter)))
                {
                    string comment = line.Substring(i + 1);

                    yield return(new LexToken(LexToken.Types.Comment, comment, lineIndex, i, line.Length - 1, indentation, IsFirst()));

                    yield break;
                }
                else if (line[i] == KeyValueSeparator)
                {
                    string value = TakeWhile(line, i + 1, o => o != CommentBegin);

                    if (!string.IsNullOrWhiteSpace(value))
                    {
                        yield return(ParseValue(value, i, 1));
                    }

                    i += value.Length;
                }
                else if (line[i] == ListItemBegin)
                {
                    yield return(new LexToken(LexToken.Types.BeginListItem, null, lineIndex, i, i + 1, indentation, IsFirst()));

                    string value = TakeWhile(line, i + 1, o => o != CommentBegin);

                    if (!string.IsNullOrWhiteSpace(value))
                    {
                        yield return(ParseValue(value, i, 0));
                    }

                    i++; //Skip list item begin char
                    i += value.Length;

                    if (value.EndsWith(" "))
                    {
                        i--;
                    }
                }
                else if (line[i] != IndentationChar || !onIndentation)
                {
                    onIndentation = false;

                    string key = TakeWhile(line, i, o => o != CommentBegin && o != KeyValueSeparator);

                    yield return(new LexToken(LexToken.Types.Key, key.Trim(), lineIndex, i, i + key.Length, indentation, IsFirst()));

                    i += key.Length - 1;
                }
            }

            LexToken ParseValue(string value, int i, int startOffset)
            {
                if (value.Trim() == BeginMultilineString)
                {
                    state.InMultiline = true;

                    return(new LexToken(LexToken.Types.BeginMultilineString, null, lineIndex, i + startOffset, i + 1 + value.Length, indentation, IsFirst()));
                }

                return(new LexToken(LexToken.Types.Value, value.Trim(), lineIndex, i + startOffset, i + 1 + value.Length, indentation, IsFirst()));
            }

            bool IsFirst() => hasAddedAny ? false : hasAddedAny = true;
        }