예제 #1
0
        public bool EatItemSeperatorOrMapEndChar(StringSegment value, ref int i)
        {
            for (; i < value.Length; i++)
            {
                var c = value.GetChar(i); if (!JsonUtils.IsWhiteSpace(c))
                {
                    break;
                }
            }                                                                                                  //Whitespace inline

            if (i == value.Length)
            {
                return(false);
            }

            var success = value.GetChar(i) == JsWriter.ItemSeperator ||
                          value.GetChar(i) == JsWriter.MapEndChar;

            i++;

            if (success)
            {
                for (; i < value.Length; i++)
                {
                    var c = value.GetChar(i); if (!JsonUtils.IsWhiteSpace(c))
                    {
                        break;
                    }
                }                                                                                                  //Whitespace inline
            }

            return(success);
        }
예제 #2
0
        public static object ObjectStringToType(StringSegment strType)
        {
            var type = ExtractType(strType);

            if (type != null)
            {
                var parseFn       = Serializer.GetParseStringSegmentFn(type);
                var propertyValue = parseFn(strType);
                return(propertyValue);
            }

            if (JsConfig.ConvertObjectTypesIntoStringDictionary && !strType.IsNullOrEmpty())
            {
                if (strType.GetChar(0) == JsWriter.MapStartChar)
                {
                    var dynamicMatch = DeserializeDictionary <TSerializer> .ParseDictionary <string, object>(strType, null, v => Serializer.UnescapeString(v).Value, v => Serializer.UnescapeString(v).Value);

                    if (dynamicMatch != null && dynamicMatch.Count > 0)
                    {
                        return(dynamicMatch);
                    }
                }

                if (strType.GetChar(0) == JsWriter.ListStartChar)
                {
                    return(DeserializeList <List <object>, TSerializer> .ParseStringSegment(strType));
                }
            }

            return((JsConfig.TryToParsePrimitiveTypeValues
                ? ParsePrimitive(strType.Value)
                : null) ?? Serializer.UnescapeString(strType).Value);
        }
예제 #3
0
 public StringSegment UnescapeSafeString(StringSegment value)
 {
     if (value.IsNullOrEmpty())
     {
         return(value);
     }
     return(value.GetChar(0) == JsonUtils.QuoteChar && value.GetChar(value.Length - 1) == JsonUtils.QuoteChar
         ? value.Subsegment(1, value.Length - 2)
         : value);
 }
예제 #4
0
 public bool EatMapStartChar(StringSegment value, ref int i)
 {
     for (; i < value.Length; i++)
     {
         var c = value.GetChar(i); if (!JsonUtils.IsWhiteSpace(c))
         {
             break;
         }
     }                                                                                                  //Whitespace inline
     return(value.GetChar(i++) == JsWriter.MapStartChar);
 }
예제 #5
0
        public bool EatItemSeperatorOrMapEndChar(StringSegment value, ref int i)
        {
            if (i == value.Length)
            {
                return(false);
            }

            var success = value.GetChar(i) == JsWriter.ItemSeperator ||
                          value.GetChar(i) == JsWriter.MapEndChar;

            i++;
            return(success);
        }
예제 #6
0
        public static StringSegment ParseJsCallExpression(this StringSegment literal, out JsCallExpression expression, bool filterExpression = false)
        {
            literal = literal.ParseIdentifier(out var token);

            if (!(token is JsIdentifier identifier))
            {
                throw new SyntaxErrorException($"Expected identifier but instead found '{token}'");
            }

            literal = literal.AdvancePastWhitespace();

            if (literal.IsNullOrEmpty() || literal.GetChar(0) != '(')
            {
                var isWhitespaceSyntax = filterExpression && literal.GetChar(0) == ':';
                if (isWhitespaceSyntax)
                {
                    literal = literal.Advance(1);

                    // replace everything after ':' up till new line and rewrite as single string to method
                    var endStringPos    = literal.IndexOf("\n");
                    var endStatementPos = literal.IndexOf("}}");

                    if (endStringPos == -1 || (endStatementPos != -1 && endStatementPos < endStringPos))
                    {
                        endStringPos = endStatementPos;
                    }

                    if (endStringPos == -1)
                    {
                        throw new SyntaxErrorException($"Whitespace sensitive syntax did not find a '\\n' new line to mark the end of the statement, near '{literal.SubstringWithElipsis(0,50)}'");
                    }

                    var originalArg   = literal.Subsegment(0, endStringPos).Trim().ToString();
                    var rewrittenArgs = originalArg.Replace("{", "{{").Replace("}", "}}");
                    var strArg        = new JsLiteral(rewrittenArgs);

                    expression = new JsCallExpression(identifier, strArg);
                    return(literal.Subsegment(endStringPos));
                }

                expression = new JsCallExpression(identifier);
                return(literal);
            }

            literal.Advance(1);

            literal = literal.ParseArguments(out var args, termination: ')');

            expression = new JsCallExpression(identifier, args.ToArray());
            return(literal);
        }
예제 #7
0
        internal static StringSegment ParseArguments(this StringSegment literal, out List <JsToken> arguments, char termination)
        {
            arguments = new List <JsToken>();

            literal = literal.Advance(1);
            while (!literal.IsNullOrEmpty())
            {
                literal = literal.AdvancePastWhitespace();
                if (literal.GetChar(0) == termination)
                {
                    literal = literal.Advance(1);
                    break;
                }

                literal = literal.ParseJsExpression(out var listValue);
                arguments.Add(listValue);

                literal = literal.AdvancePastWhitespace();
                if (literal.IsNullOrEmpty())
                {
                    break;
                }

                if (literal.GetChar(0) == termination)
                {
                    literal = literal.Advance(1);
                    break;
                }

                literal = literal.AdvancePastWhitespace();
                var c = literal.GetChar(0);
                if (c == termination)
                {
                    literal = literal.Advance(1);
                    break;
                }

                if (c != ',')
                {
                    throw new ArgumentException($"Unterminated arguments expression near: {literal.SubstringWithElipsis(0, 50)}");
                }

                literal = literal.Advance(1);
                literal = literal.AdvancePastWhitespace();
            }

            literal = literal.AdvancePastWhitespace();

            return(literal);
        }
예제 #8
0
 public static bool IsEmptyMap(StringSegment value, int i = 1)
 {
     for (; i < value.Length; i++)
     {
         var c = value.GetChar(i); if (!JsonUtils.IsWhiteSpace(c))
         {
             break;
         }
     }                                                                                                  //Whitespace inline
     if (value.Length == i)
     {
         return(true);
     }
     return(value.GetChar(i++) == JsWriter.MapEndChar);
 }
예제 #9
0
        public StringSegment UnescapeSafeString(StringSegment value)
        {
            if (value.IsNullOrEmpty())
            {
                return(value);
            }
            return(value.GetChar(0) == JsonUtils.QuoteChar && value.GetChar(value.Length - 1) == JsonUtils.QuoteChar
                ? value.Subsegment(1, value.Length - 2)
                : value);

            //if (value[0] != JsonUtils.QuoteChar)
            //    throw new Exception("Invalid unquoted string starting with: " + value.SafeSubstring(50));

            //return value.Substring(1, value.Length - 2);
        }
예제 #10
0
 public bool EatMapKeySeperator(StringSegment value, ref int i)
 {
     for (; i < value.Length; i++)
     {
         var c = value.GetChar(i); if (!JsonUtils.IsWhiteSpace(c))
         {
             break;
         }
     }                                                                                                  //Whitespace inline
     if (value.Length == i)
     {
         return(false);
     }
     return(value.GetChar(i++) == JsWriter.MapKeySeperator);
 }
예제 #11
0
        public StringSegment EatMapKey(StringSegment value, ref int i)
        {
            var valueLength = value.Length;

            for (; i < value.Length; i++)
            {
                var c = value.GetChar(i); if (!JsonUtils.IsWhiteSpace(c))
                {
                    break;
                }
            }                                                                                                  //Whitespace inline

            var tokenStartPos = i;
            var valueChar     = value.GetChar(i);

            switch (valueChar)
            {
            //If we are at the end, return.
            case JsWriter.ItemSeperator:
            case JsWriter.MapEndChar:
                return(default(StringSegment));

            //Is Within Quotes, i.e. "..."
            case JsWriter.QuoteChar:
                return(ParseString(value, ref i));
            }

            //Is Value
            while (++i < valueLength)
            {
                valueChar = value.GetChar(i);

                if (valueChar == JsWriter.ItemSeperator
                    //If it doesn't have quotes it's either a keyword or number so also has a ws boundary
                    || (JsonUtils.IsWhiteSpace(valueChar))
                    )
                {
                    break;
                }
            }

            return(value.Subsegment(tokenStartPos, i - tokenStartPos));
        }
예제 #12
0
        public bool EatMapStartChar(StringSegment value, ref int i)
        {
            var success = value.GetChar(i) == JsWriter.MapStartChar;

            if (success)
            {
                i++;
            }
            return(success);
        }
예제 #13
0
 public void EatWhitespace(StringSegment value, ref int i)
 {
     for (; i < value.Length; i++)
     {
         var c = value.GetChar(i); if (!JsonUtils.IsWhiteSpace(c))
         {
             break;
         }
     }                                                                                                  //Whitespace inline
 }
예제 #14
0
        public static StringSegment AdvancePastWhitespace(this StringSegment literal)
        {
            var i = 0;

            while (i < literal.Length && literal.GetChar(i).IsWhiteSpace())
            {
                i++;
            }

            return(i == 0 ? literal : literal.Subsegment(i < literal.Length ? i : literal.Length));
        }
예제 #15
0
        internal static StringSegment ParseJsonString(StringSegment json, ref int index)
        {
            for (; index < json.Length; index++)
            {
                var ch = json.GetChar(index); if (!JsonUtils.IsWhiteSpace(ch))
                {
                    break;
                }
            }                                                                                                              //Whitespace inline

            return(UnEscapeJsonString(json, ref index));
        }
예제 #16
0
        private static StringSegment UnEscapeJsonString(StringSegment json, ref int index)
        {
            if (json.IsNullOrEmpty())
            {
                return(json);
            }
            var jsonLength = json.Length;
            var buffer     = json.Buffer;
            var offset     = json.Offset;

            var firstChar = buffer[offset + index];

            if (firstChar == JsonUtils.QuoteChar)
            {
                index++;

                // MicroOp: See if we can short-circuit evaluation (to avoid StringBuilder)
                var strEndPos = json.IndexOfAny(IsSafeJsonChars, index);
                if (strEndPos == -1)
                {
                    return(json.Subsegment(index, jsonLength - index));
                }

                if (json.GetChar(strEndPos) == JsonUtils.QuoteChar)
                {
                    var potentialValue = json.Subsegment(index, strEndPos - index);
                    index = strEndPos + 1;
                    return(potentialValue);
                }
            }
            else
            {
                var i   = index + offset;
                var end = offset + jsonLength;

                while (i < end)
                {
                    var c = buffer[i];
                    if (c == JsonUtils.QuoteChar || c == JsonUtils.EscapeChar)
                    {
                        break;
                    }
                    i++;
                }

                if (i == end)
                {
                    return(new StringSegment(buffer, offset + index, jsonLength - index));
                }
            }

            return(Unescape(json));
        }
예제 #17
0
        static int GetNextBinaryPrecedence(this StringSegment literal)
        {
            if (!literal.IsNullOrEmpty() && !literal.GetChar(0).IsExpressionTerminatorChar())
            {
                literal.ParseJsBinaryOperator(out var binaryOp);
                if (binaryOp != null)
                {
                    return(JsTokenUtils.GetBinaryPrecedence(binaryOp.Token));
                }
            }

            return(0);
        }
예제 #18
0
        internal static int IndexOfNextCharNotInQuotes(this StringSegment text, int varStartPos, char c1, char c2)
        {
            var inDoubleQuotes = false;
            var inSingleQuotes = false;

            for (var i = varStartPos; i < text.Length; i++)
            {
                var c = text.GetChar(i);
                if (c.IsWhiteSpace())
                {
                    continue;
                }

                if (inDoubleQuotes)
                {
                    if (c == '"')
                    {
                        inDoubleQuotes = false;
                    }
                    continue;
                }
                if (inSingleQuotes)
                {
                    if (c == '\'')
                    {
                        inSingleQuotes = false;
                    }
                    continue;
                }
                if (c == '"')
                {
                    inDoubleQuotes = true;
                    continue;
                }
                if (c == '\'')
                {
                    inSingleQuotes = true;
                    continue;
                }

                if (c == c1 || c == c2)
                {
                    return(i);
                }
            }

            return(text.Length);
        }
예제 #19
0
        public static StringSegment AdvancePastChar(this StringSegment literal, char delim)
        {
            var i = 0;
            var c = (char)0;

            while (i < literal.Length && (c = literal.GetChar(i)) != delim)
            {
                i++;
            }

            if (c == delim)
            {
                return(literal.Subsegment(i + 1));
            }

            return(i == 0 ? literal : literal.Subsegment(i < literal.Length ? i : literal.Length));
        }
예제 #20
0
        static int GetNextBinaryPrecedence(this StringSegment literal)
        {
            if (!literal.IsNullOrEmpty())
            {
                var c = literal.GetChar(0);
                if (!JsTokenUtils.ExpressionTerminator.Contains(c))
                {
                    literal.ParseJsToken(out var token);

                    if (token is JsBinaryOperator binaryOp)
                    {
                        return(JsTokenUtils.GetBinaryPrecedence(binaryOp.Token));
                    }
                }
            }

            return(0);
        }
예제 #21
0
        // TODO: optimize ExtractType
        public static Type ExtractType(StringSegment strType)
        {
            if (!strType.HasValue || strType.Length <= 1)
            {
                return(null);
            }

            var hasWhitespace = Json.JsonUtils.WhiteSpaceChars.Contains(strType.GetChar(1));

            if (hasWhitespace)
            {
                var pos = strType.IndexOf('"');
                if (pos >= 0)
                {
                    strType = new StringSegment($"{{{strType.Substring(pos, strType.Length - pos)}");
                }
            }

            var typeAttrInObject = Serializer.TypeAttrInObject;

            if (strType.Length > typeAttrInObject.Length &&
                strType.Substring(0, typeAttrInObject.Length) == typeAttrInObject)
            {
                var propIndex = typeAttrInObject.Length;
                var typeName  = Serializer.UnescapeSafeString(Serializer.EatValue(strType, ref propIndex)).Value;

                var type = JsConfig.TypeFinder(typeName);

                JsWriter.AssertAllowedRuntimeType(type);

                if (type == null)
                {
                    Tracer.Instance.WriteWarning($"Could not find type: {typeName}");
                    return(null);
                }

                return(PclExport.Instance.UseType(type));
            }

            return(null);
        }
        public static StringSegment StripList(StringSegment value)
        {
            if (value.IsNullOrEmpty())
            {
                return(default(StringSegment));
            }

            value = value.Trim();

            const int startQuotePos = 1;
            const int endQuotePos   = 2;
            var       ret           = value.GetChar(0) == JsWriter.ListStartChar
                    ? value.Subsegment(startQuotePos, value.Length - endQuotePos)
                    : value;
            var pos = 0;

            Serializer.EatWhitespace(ret, ref pos);
            var val = ret.Subsegment(pos, ret.Length - pos);

            return(val);
        }
예제 #23
0
        public static Type ExtractType(ITypeSerializer Serializer, StringSegment strType)
        {
            if (!strType.HasValue || strType.Length <= 1)
            {
                return(null);
            }

            var hasWhitespace = Json.JsonUtils.WhiteSpaceChars.Contains(strType.GetChar(1));

            if (hasWhitespace)
            {
                var pos = strType.IndexOf('"');
                if (pos >= 0)
                {
                    strType = new StringSegment("{" + strType.Substring(pos));
                }
            }

            var typeAttrInObject = Serializer.TypeAttrInObject;

            if (strType.Length > typeAttrInObject.Length &&
                strType.Substring(0, typeAttrInObject.Length) == typeAttrInObject)
            {
                var propIndex = typeAttrInObject.Length;
                var typeName  = Serializer.EatValue(strType, ref propIndex).Value;
                var type      = JsConfig.TypeFinder(typeName);

                if (type == null)
                {
                    Tracer.Instance.WriteWarning("Could not find type: " + typeName);
                }

                return(type);
            }

            return(null);
        }
예제 #24
0
        public static StringSegment ParseBinaryExpression(this StringSegment literal, out JsExpression expr, bool filterExpression)
        {
            literal = literal.AdvancePastWhitespace();

            literal = literal.ParseJsToken(out var lhs, filterExpression: filterExpression);

            if (literal.IsNullOrEmpty())
            {
                expr = lhs is JsExpression jsExpr
                    ? jsExpr
                    : throw new SyntaxErrorException($"Expected Expression but was {lhs.DebugToken()}");
            }
            else
            {
                literal = literal.ParseJsBinaryOperator(out var op);

                if (op == null)
                {
                    throw new SyntaxErrorException($"Expected binary operator near: {literal.DebugLiteral()}");
                }

                var prec = JsTokenUtils.GetBinaryPrecedence(op.Token);
                if (prec > 0)
                {
                    literal = literal.ParseJsToken(out JsToken rhs, filterExpression: filterExpression);

                    var stack = new Stack <JsToken>();
                    stack.Push(lhs);
                    stack.Push(op);
                    stack.Push(rhs);

                    var precedences = new List <int> {
                        prec
                    };

                    while (true)
                    {
                        literal = literal.AdvancePastWhitespace();
                        if (filterExpression && literal.Length > 2 && (literal.GetChar(0) == '|' && literal.GetChar(1) != '|'))
                        {
                            break;
                        }

                        prec = literal.GetNextBinaryPrecedence();
                        if (prec == 0)
                        {
                            break;
                        }

                        while ((stack.Count > 2) && prec <= precedences[precedences.Count - 1])
                        {
                            rhs = stack.Pop();
                            var operand = (JsBinaryOperator)stack.Pop();
                            precedences.RemoveAt(precedences.Count - 1);
                            lhs = stack.Pop();
                            stack.Push(CreateJsExpression(lhs, operand, rhs));
                        }

                        literal = literal.ParseJsBinaryOperator(out op);

                        if (literal.IsNullOrEmpty())
                        {
                            throw new SyntaxErrorException($"Expected expression near: '{literal.DebugLiteral()}'");
                        }

                        literal = literal.ParseJsToken(out var token, filterExpression: filterExpression);

                        stack.Push(op);
                        stack.Push(token);
                        precedences.Add(prec);
                    }

                    var i   = stack.Count - 1;
                    var ret = stack.Pop();

                    while (stack.Count > 0)
                    {
                        op  = (JsBinaryOperator)stack.Pop();
                        lhs = stack.Pop();
                        ret = CreateJsExpression(lhs, op, ret);
                    }

                    expr = (JsExpression)ret;
                }
                else
                {
                    expr = lhs is JsExpression jsExpr
                        ? jsExpr
                        : throw new SyntaxErrorException($"Expected Expression but was {lhs.DebugToken()}");
                }
            }

            return(literal);
        }
예제 #25
0
        public static IDictionary <TKey, TValue> ParseDictionary <TKey, TValue>(
            StringSegment value, Type createMapType,
            ParseStringSegmentDelegate parseKeyFn, ParseStringSegmentDelegate parseValueFn)
        {
            if (!value.HasValue)
            {
                return(null);
            }

            var tryToParseItemsAsDictionaries =
                JsConfig.ConvertObjectTypesIntoStringDictionary && typeof(TValue) == typeof(object);
            var tryToParseItemsAsPrimitiveTypes =
                JsConfig.TryToParsePrimitiveTypeValues && typeof(TValue) == typeof(object);

            var index = VerifyAndGetStartIndex(value, createMapType);

            var to = createMapType == null
                ? new Dictionary <TKey, TValue>()
                : (IDictionary <TKey, TValue>)createMapType.CreateInstance();

            if (JsonTypeSerializer.IsEmptyMap(value, index))
            {
                return(to);
            }

            var valueLength = value.Length;

            while (index < valueLength)
            {
                var keyValue = Serializer.EatMapKey(value, ref index);
                Serializer.EatMapKeySeperator(value, ref index);
                var elementStartIndex = index;
                var elementValue      = Serializer.EatTypeValue(value, ref index);
                if (!keyValue.HasValue)
                {
                    continue;
                }

                var mapKey = (TKey)parseKeyFn(keyValue);

                if (tryToParseItemsAsDictionaries)
                {
                    Serializer.EatWhitespace(value, ref elementStartIndex);
                    if (elementStartIndex < valueLength && value.GetChar(elementStartIndex) == JsWriter.MapStartChar)
                    {
                        var tmpMap = ParseDictionary <TKey, TValue>(elementValue, createMapType, parseKeyFn, parseValueFn);
                        if (tmpMap != null && tmpMap.Count > 0)
                        {
                            to[mapKey] = (TValue)tmpMap;
                        }
                    }
                    else if (elementStartIndex < valueLength && value.GetChar(elementStartIndex) == JsWriter.ListStartChar)
                    {
                        to[mapKey] = (TValue)DeserializeList <List <object>, TSerializer> .ParseStringSegment(elementValue);
                    }
                    else
                    {
                        to[mapKey] = (TValue)(tryToParseItemsAsPrimitiveTypes && elementStartIndex < valueLength
                                        ? DeserializeType <TSerializer> .ParsePrimitive(elementValue.Value, value.GetChar(elementStartIndex))
                                        : parseValueFn(elementValue));
                    }
                }
                else
                {
                    if (tryToParseItemsAsPrimitiveTypes && elementStartIndex < valueLength)
                    {
                        Serializer.EatWhitespace(value, ref elementStartIndex);
                        to[mapKey] = (TValue)DeserializeType <TSerializer> .ParsePrimitive(elementValue.Value, value.GetChar(elementStartIndex));
                    }
                    else
                    {
                        to[mapKey] = (TValue)parseValueFn(elementValue);
                    }
                }

                Serializer.EatItemSeperatorOrMapEndChar(value, ref index);
            }

            return(to);
        }
예제 #26
0
        public static IDictionary ParseIDictionary(StringSegment value, Type dictType)
        {
            if (!value.HasValue)
            {
                return(null);
            }

            var index = VerifyAndGetStartIndex(value, dictType);

            var valueParseMethod = Serializer.GetParseStringSegmentFn(typeof(object));

            if (valueParseMethod == null)
            {
                return(null);
            }

            var to = (IDictionary)dictType.CreateInstance();

            if (JsonTypeSerializer.IsEmptyMap(value, index))
            {
                return(to);
            }

            var valueLength = value.Length;

            while (index < valueLength)
            {
                var keyValue = Serializer.EatMapKey(value, ref index);
                Serializer.EatMapKeySeperator(value, ref index);
                var elementStartIndex = index;
                var elementValue      = Serializer.EatTypeValue(value, ref index);
                if (!keyValue.HasValue)
                {
                    continue;
                }

                var mapKey = valueParseMethod(keyValue);

                if (elementStartIndex < valueLength)
                {
                    Serializer.EatWhitespace(value, ref elementStartIndex);
                    to[mapKey] = DeserializeType <TSerializer> .ParsePrimitive(elementValue.Value, value.GetChar(elementStartIndex));
                }
                else
                {
                    to[mapKey] = valueParseMethod(elementValue);
                }

                Serializer.EatItemSeperatorOrMapEndChar(value, ref index);
            }

            return(to);
        }
예제 #27
0
        public static StringSegment ParseNextExpression(this StringSegment literal, out JsExpression binding)
        {
            var inDoubleQuotes   = false;
            var inSingleQuotes   = false;
            var inBackTickQuotes = false;
            var inBrackets       = 0;
            var inBraces         = 0;
            var lastPos          = 0;

            for (var i = 0; i < literal.Length; i++)
            {
                var c = literal.GetChar(i);
                if (c.IsWhiteSpace())
                {
                    continue;
                }

                if (inDoubleQuotes)
                {
                    if (c == '"')
                    {
                        inDoubleQuotes = false;
                    }
                    continue;
                }
                if (inSingleQuotes)
                {
                    if (c == '\'')
                    {
                        inSingleQuotes = false;
                    }
                    continue;
                }
                if (inBackTickQuotes)
                {
                    if (c == '`')
                    {
                        inBackTickQuotes = false;
                    }
                    continue;
                }
                if (inBrackets > 0)
                {
                    if (c == '[')
                    {
                        ++inBrackets;
                    }
                    if (c == ']')
                    {
                        --inBrackets;
                    }
                    continue;
                }
                if (inBraces > 0)
                {
                    if (c == '{')
                    {
                        ++inBraces;
                    }
                    if (c == '}')
                    {
                        --inBraces;
                    }
                    continue;
                }

                if (c == ':') //whitespace sensitive syntax
                {
                    // replace everything after ':' up till new line and rewrite as single string to method
                    var endStringPos    = literal.IndexOf("\n", i);
                    var endStatementPos = literal.IndexOf("}}", i);

                    if (endStringPos == -1 || (endStatementPos != -1 && endStatementPos < endStringPos))
                    {
                        endStringPos = endStatementPos;
                    }

                    if (endStringPos == -1)
                    {
                        throw new NotSupportedException($"Whitespace sensitive syntax did not find a '\\n' new line to mark the end of the statement, near '{literal.SubstringWithElipsis(i,50)}'");
                    }

                    binding = new JsExpression(literal.Subsegment(0, i).Trim());

                    var originalArgs  = literal.Substring(i + 1, endStringPos - i - 1);
                    var rewrittenArgs = "`" + originalArgs.Trim().Replace("{", "{{").Replace("}", "}}").Replace("`", "\\`") + "`)";
                    ParseArguments(rewrittenArgs.ToStringSegment(), out List <StringSegment> args);
                    binding.Args = args;
                    return(literal.Subsegment(endStringPos));
                }

                if (c == '(')
                {
                    var pos = i + 1;
                    binding      = new JsExpression(literal.Subsegment(0, i).Trim());
                    literal      = ParseArguments(literal.Subsegment(pos), out List <StringSegment> args);
                    binding.Args = args;
                    return(literal.Advance(1));
                }

                switch (c)
                {
                case '"':
                    inDoubleQuotes = true;
                    continue;

                case '\'':
                    inSingleQuotes = true;
                    continue;

                case '`':
                    inBackTickQuotes = true;
                    continue;

                case '[':
                    inBrackets++;
                    continue;

                case '{':
                    inBraces++;
                    continue;
                }

                if (!(c.IsValidVarNameChar() || c.IsBindingExpressionChar()))
                {
                    binding = new JsExpression(literal.Subsegment(lastPos, i - lastPos).Trim());
                    return(literal.Advance(i));
                }
            }

            binding = new JsExpression(literal.Subsegment(0, literal.Length));
            return(TypeConstants.EmptyStringSegment);
        }
예제 #28
0
        public static StringSegment ParseNextToken(this StringSegment literal, out object value, out JsBinding binding, bool allowWhitespaceSyntax)
        {
            binding = null;
            value   = null;
            var c = (char)0;

            if (literal.IsNullOrEmpty())
            {
                return(TypeConstants.EmptyStringSegment);
            }

            var i = 0;

            literal = literal.AdvancePastWhitespace();

            var firstChar = literal.GetChar(0);

            if (firstChar == '\'' || firstChar == '"' || firstChar == '`')
            {
                i = 1;
                var hasEscapeChar = false;
                while (i < literal.Length && ((c = literal.GetChar(i)) != firstChar || literal.GetChar(i - 1) == '\\'))
                {
                    i++;
                    if (!hasEscapeChar)
                    {
                        hasEscapeChar = c == '\\';
                    }
                }

                if (i >= literal.Length || literal.GetChar(i) != firstChar)
                {
                    throw new ArgumentException($"Unterminated string literal: {literal}");
                }

                var str = literal.Substring(1, i - 1);
                value = str;

                if (hasEscapeChar)
                {
                    var sb = StringBuilderCache.Allocate();
                    for (var j = 0; j < str.Length; j++)
                    {
                        // strip the back-slash used to escape quote char in strings
                        var ch = str[j];
                        if (ch != '\\' || (j + 1 >= str.Length || str[j + 1] != firstChar))
                        {
                            sb.Append(ch);
                        }
                    }
                    value = StringBuilderCache.ReturnAndFree(sb);
                }

                return(literal.Advance(i + 1));
            }
            if (firstChar >= '0' && firstChar <= '9' || (literal.Length >= 2 && (firstChar == '-' || firstChar == '+') && literal.GetChar(1).IsNumericChar()))
            {
                i = 1;
                var hasExponent = false;
                var hasDecimal  = false;

                while (i < literal.Length && IsNumericChar(c = literal.GetChar(i)) ||
                       (hasExponent = (c == 'e' || c == 'E')))
                {
                    if (c == '.')
                    {
                        hasDecimal = true;
                    }

                    i++;

                    if (hasExponent)
                    {
                        i += 2; // [e+1]0

                        while (i < literal.Length && IsNumericChar(literal.GetChar(i)))
                        {
                            i++;
                        }

                        break;
                    }
                }

                var numLiteral = literal.Subsegment(0, i);

                //don't convert into ternary to avoid Type coercion
                if (hasDecimal || hasExponent)
                {
                    value = numLiteral.TryParseDouble(out double d) ? d : default(double);
                }
                else
                {
                    value = numLiteral.ParseSignedInteger();
                }

                return(literal.Advance(i));
            }
            if (firstChar == '{')
            {
                var map = new Dictionary <string, object>();

                literal = literal.Advance(1);
                while (!literal.IsNullOrEmpty())
                {
                    literal = literal.AdvancePastWhitespace();
                    if (literal.GetChar(0) == '}')
                    {
                        literal = literal.Advance(1);
                        break;
                    }

                    literal = literal.ParseNextToken(out object mapKeyString, out JsBinding mapKeyVar);

                    if (mapKeyVar is JsExpression)
                    {
                        throw new NotSupportedException($"JsExpression '{mapKeyVar?.Binding}' is not a valid Object key.");
                    }

                    var mapKey = mapKeyVar != null
                        ? mapKeyVar.Binding.Value
                        : (string)mapKeyString;

                    if (mapKey != null)
                    {
                        literal = literal.AdvancePastWhitespace();
                        if (literal.Length > 0 && literal.GetChar(0) == ':')
                        {
                            literal     = literal.Advance(1);
                            literal     = literal.ParseNextToken(out object mapValue, out JsBinding mapValueBinding);
                            map[mapKey] = mapValue ?? mapValueBinding;
                        }
                        else //shorthand notation
                        {
                            if (literal.Length == 0 || (c = literal.GetChar(0)) != ',' && c != '}')
                            {
                                throw new ArgumentException($"Unterminated object literal near: {literal.SubstringWithElipsis(0, 50)}");
                            }

                            map[mapKey] = new JsBinding(mapKey);
                        }
                    }

                    literal = literal.AdvancePastWhitespace();
                    if (literal.IsNullOrEmpty())
                    {
                        break;
                    }

                    if (literal.GetChar(0) == '}')
                    {
                        literal = literal.Advance(1);
                        break;
                    }

                    literal = literal.AdvancePastChar(',');
                    literal = literal.AdvancePastWhitespace();
                }

                value = map;
                return(literal);
            }
            if (firstChar == '[')
            {
                var list = new List <object>();

                literal = literal.Advance(1);
                while (!literal.IsNullOrEmpty())
                {
                    literal = literal.AdvancePastWhitespace();
                    if (literal.GetChar(0) == ']')
                    {
                        literal = literal.Advance(1);
                        break;
                    }

                    literal = literal.ParseNextToken(out object mapValue, out JsBinding mapVarRef);
                    list.Add(mapVarRef ?? mapValue);

                    literal = literal.AdvancePastWhitespace();
                    if (literal.IsNullOrEmpty())
                    {
                        break;
                    }

                    if (literal.GetChar(0) == ']')
                    {
                        literal = literal.Advance(1);
                        break;
                    }

                    literal = literal.AdvancePastWhitespace();
                    c       = literal.GetChar(0);
                    if (c == ']')
                    {
                        literal = literal.Advance(1);
                        break;
                    }

                    if (c != ',')
                    {
                        throw new ArgumentException($"Unterminated array literal near: {literal.SubstringWithElipsis(0, 50)}");
                    }

                    literal = literal.Advance(1);
                    literal = literal.AdvancePastWhitespace();
                }

                literal = literal.AdvancePastWhitespace();

                value = list;
                return(literal);
            }
            if (literal.StartsWith("true") && (literal.Length == 4 || !IsValidVarNameChar(literal.GetChar(4))))
            {
                value = true;
                return(literal.Advance(4));
            }
            if (literal.StartsWith("false") && (literal.Length == 5 || !IsValidVarNameChar(literal.GetChar(5))))
            {
                value = false;
                return(literal.Advance(5));
            }
            if (literal.StartsWith("null") && (literal.Length == 4 || !IsValidVarNameChar(literal.GetChar(4))))
            {
                value = JsNull.Value;
                return(literal.Advance(4));
            }
            if (firstChar.IsOperatorChar())
            {
                if (literal.StartsWith(">="))
                {
                    binding = JsGreaterThanEqual.Operand;
                    return(literal.Advance(2));
                }
                if (literal.StartsWith("<="))
                {
                    binding = JsLessThanEqual.Operand;
                    return(literal.Advance(2));
                }
                if (literal.StartsWith("!=="))
                {
                    binding = JsStrictNotEquals.Operand;
                    return(literal.Advance(3));
                }
                if (literal.StartsWith("!="))
                {
                    binding = JsNotEquals.Operand;
                    return(literal.Advance(2));
                }
                if (literal.StartsWith("==="))
                {
                    binding = JsStrictEquals.Operand;
                    return(literal.Advance(3));
                }
                if (literal.StartsWith("=="))
                {
                    binding = JsEquals.Operand;
                    return(literal.Advance(2));
                }
                if (literal.StartsWith("||"))
                {
                    binding = JsOr.Operator;
                    return(literal.Advance(2));
                }
                if (literal.StartsWith("&&"))
                {
                    binding = JsAnd.Operator;
                    return(literal.Advance(2));
                }

                switch (firstChar)
                {
                case '>':
                    binding = JsGreaterThan.Operand;
                    return(literal.Advance(1));

                case '<':
                    binding = JsLessThan.Operand;
                    return(literal.Advance(1));

                case '=':
                    binding = JsAssignment.Operator;
                    return(literal.Advance(1));

                case '!':
                    binding = JsNot.Operator;
                    return(literal.Advance(1));

                case '+':
                    binding = JsAddition.Operator;
                    return(literal.Advance(1));

                case '-':
                    binding = JsSubtraction.Operator;
                    return(literal.Advance(1));

                case '*':
                    binding = JsMultiplication.Operator;
                    return(literal.Advance(1));

                case '\\':
                    binding = JsDivision.Operator;
                    return(literal.Advance(1));

                case '|':
                    binding = JsBitwiseOr.Operator;
                    return(literal.Advance(1));

                case '&':
                    binding = JsBitwiseAnd.Operator;
                    return(literal.Advance(1));

                default:
                    throw new NotSupportedException($"Invalid Operator found near: '{literal.SubstringWithElipsis(0, 50)}'");
                }
            }

            // name
            i = 1;
            var isExpression  = false;
            var hadWhitespace = false;

            while (i < literal.Length && IsValidVarNameChar(c = literal.GetChar(i)) ||
                   (isExpression = c.IsBindingExpressionChar() || (allowWhitespaceSyntax && c == ':')))
            {
                if (isExpression)
                {
                    literal = literal.ParseNextExpression(out JsExpression expr);
                    binding = expr;
                    return(literal);
                }

                i++;

                while (i < literal.Length && literal.GetChar(i).IsWhiteSpace()) // advance past whitespace
                {
                    i++;
                    hadWhitespace = true;
                }

                if (hadWhitespace && (i >= literal.Length || !literal.GetChar(i).IsBindingExpressionChar()))
                {
                    break;
                }
            }

            binding = new JsBinding(literal.Subsegment(0, i).TrimEnd());
            return(literal.Advance(i));
        }
예제 #29
0
        public static object ParseNumber(this StringSegment value, bool bestFit)
        {
            if (value.Length == 1)
            {
                int singleDigit = value.GetChar(0);
                if (singleDigit >= 48 || singleDigit <= 57) // 0 - 9
                {
                    var result = singleDigit - 48;
                    if (bestFit)
                    {
                        return((byte)result);
                    }
                    return(result);
                }
            }

            // Parse as decimal
            var acceptDecimal = JsConfig.ParsePrimitiveFloatingPointTypes.Has(ParseAsType.Decimal);
            var isDecimal     = value.TryParseDecimal(out decimal decimalValue);

            // Check if the number is an Primitive Integer type given that we have a decimal
            if (isDecimal && decimalValue == decimal.Truncate(decimalValue))
            {
                // Value is a whole number
                var parseAs = JsConfig.ParsePrimitiveIntegerTypes;
                if (parseAs.Has(ParseAsType.Byte) && decimalValue <= byte.MaxValue && decimalValue >= byte.MinValue)
                {
                    return((byte)decimalValue);
                }
                if (parseAs.Has(ParseAsType.SByte) && decimalValue <= sbyte.MaxValue && decimalValue >= sbyte.MinValue)
                {
                    return((sbyte)decimalValue);
                }
                if (parseAs.Has(ParseAsType.Int16) && decimalValue <= Int16.MaxValue && decimalValue >= Int16.MinValue)
                {
                    return((Int16)decimalValue);
                }
                if (parseAs.Has(ParseAsType.UInt16) && decimalValue <= UInt16.MaxValue && decimalValue >= UInt16.MinValue)
                {
                    return((UInt16)decimalValue);
                }
                if (parseAs.Has(ParseAsType.Int32) && decimalValue <= Int32.MaxValue && decimalValue >= Int32.MinValue)
                {
                    return((Int32)decimalValue);
                }
                if (parseAs.Has(ParseAsType.UInt32) && decimalValue <= UInt32.MaxValue && decimalValue >= UInt32.MinValue)
                {
                    return((UInt32)decimalValue);
                }
                if (parseAs.Has(ParseAsType.Int64) && decimalValue <= Int64.MaxValue && decimalValue >= Int64.MinValue)
                {
                    return((Int64)decimalValue);
                }
                if (parseAs.Has(ParseAsType.UInt64) && decimalValue <= UInt64.MaxValue && decimalValue >= UInt64.MinValue)
                {
                    return((UInt64)decimalValue);
                }
                return(decimalValue);
            }

            // Value is a floating point number

            // Return a decimal if the user accepts a decimal
            if (isDecimal && acceptDecimal)
            {
                return(decimalValue);
            }

            var acceptFloat = JsConfig.ParsePrimitiveFloatingPointTypes.HasFlag(ParseAsType.Single);
            var isFloat     = value.TryParseFloat(out float floatValue);

            if (acceptFloat && isFloat)
            {
                return(floatValue);
            }

            var acceptDouble = JsConfig.ParsePrimitiveFloatingPointTypes.HasFlag(ParseAsType.Double);
            var isDouble     = value.TryParseDouble(out double doubleValue);

            if (acceptDouble && isDouble)
            {
                return(doubleValue);
            }

            if (isDecimal)
            {
                return(decimalValue);
            }
            if (isFloat)
            {
                return(floatValue);
            }
            if (isDouble)
            {
                return(doubleValue);
            }

            return(null);
        }
예제 #30
0
        public static StringSegment Unescape(StringSegment input, bool removeQuotes)
        {
            var length = input.Length;
            int start  = 0;
            int count  = 0;
            var output = StringBuilderThreadStatic.Allocate();

            for (; count < length;)
            {
                if (removeQuotes)
                {
                    if (input.GetChar(count) == JsonUtils.QuoteChar)
                    {
                        if (start != count)
                        {
                            output.Append(input.Buffer, input.Offset + start, count - start);
                        }
                        count++;
                        start = count;
                        continue;
                    }
                }

                if (input.GetChar(count) == JsonUtils.EscapeChar)
                {
                    if (start != count)
                    {
                        output.Append(input.Buffer, input.Offset + start, count - start);
                    }
                    start = count;
                    count++;
                    if (count >= length)
                    {
                        continue;
                    }

                    //we will always be parsing an escaped char here
                    var c = input.GetChar(count);

                    switch (c)
                    {
                    case 'a':
                        output.Append('\a');
                        count++;
                        break;

                    case 'b':
                        output.Append('\b');
                        count++;
                        break;

                    case 'f':
                        output.Append('\f');
                        count++;
                        break;

                    case 'n':
                        output.Append('\n');
                        count++;
                        break;

                    case 'r':
                        output.Append('\r');
                        count++;
                        break;

                    case 'v':
                        output.Append('\v');
                        count++;
                        break;

                    case 't':
                        output.Append('\t');
                        count++;
                        break;

                    case 'u':
                        if (count + 4 < length)
                        {
                            var unicodeString = input.Substring(count + 1, 4);
                            var unicodeIntVal = UInt32.Parse(unicodeString, NumberStyles.HexNumber);
                            output.Append(ConvertFromUtf32((int)unicodeIntVal));
                            count += 5;
                        }
                        else
                        {
                            output.Append(c);
                        }
                        break;

                    case 'x':
                        if (count + 4 < length)
                        {
                            var unicodeString = input.Substring(count + 1, 4);
                            var unicodeIntVal = uint.Parse(unicodeString, NumberStyles.HexNumber);
                            output.Append(ConvertFromUtf32((int)unicodeIntVal));
                            count += 5;
                        }
                        else
                        if (count + 2 < length)
                        {
                            var unicodeString = input.Substring(count + 1, 2);
                            var unicodeIntVal = uint.Parse(unicodeString, NumberStyles.HexNumber);
                            output.Append(ConvertFromUtf32((int)unicodeIntVal));
                            count += 3;
                        }
                        else
                        {
                            output.Append(input.Buffer, input.Offset + start, count - start);
                        }
                        break;

                    default:
                        output.Append(c);
                        count++;
                        break;
                    }
                    start = count;
                }
                else
                {
                    count++;
                }
            }
            output.Append(input.Buffer, input.Offset + start, length - start);
            return(new StringSegment(StringBuilderThreadStatic.ReturnAndFree(output)));
        }