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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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)); }
public bool EatMapStartChar(StringSegment value, ref int i) { var success = value.GetChar(i) == JsWriter.MapStartChar; if (success) { i++; } return(success); }
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 }
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)); }
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)); }
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)); }
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); }
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); }
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)); }
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); }
// 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); }
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); }
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); }
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); }
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); }
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); }
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)); }
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); }
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))); }