IKDLValue ParseValue(KDLParseContext context) // throws IOException { int c = context.Peek(); if (c == '"') { return(new KDLString(ParseEscapedString(context))); } else if (c == 'r') { return(new KDLString(ParseRawString(context))); } else if (IsValidNumericStart(c)) { return(ParseNumber(context)); } else { var stringBuilder = new StringBuilder(); while (IsLiteralChar(c)) { context.Read(); stringBuilder.AppendCodePoint(c); c = context.Peek(); } var strVal = stringBuilder.ToString(); return(strVal switch { "true" => KDLBoolean.True, "false" => KDLBoolean.False, "null" => KDLNull.Instance, _ => throw new KDLParseException(string.Format("Unknown literal in property value: '{0}' Expected 'true', 'false', or 'null'", strVal)) });
KDLDocument ParseChild(KDLParseContext context) // throws IOException { int c = context.Read(); if (c != '{') { throw new KDLInternalException(string.Format("Expected '{' but found '%s'", (char)c)); } var document = ParseDocument(context, false); switch (ConsumeWhitespaceAndLinespace(context)) { case WhitespaceResult.EndNode: throw new KDLInternalException("Got unexpected END_NODE"); case WhitespaceResult.SkipNext: throw new KDLParseException("Trailing skip markers are not allowed"); // default: //Fall through } c = context.Read(); if (c != '}') { throw new KDLParseException("No closing brace found for child"); } return(document); }
string ParseIdentifier(KDLParseContext context) // throws IOException { int c = context.Peek(); if (c == '"') { return(ParseEscapedString(context)); } else if (IsValidBareIdStart(c)) { if (c == 'r') { context.Read(); int next = context.Peek(); context.Unread('r'); if (next == '"' || next == '#') { return(ParseRawString(context)); } else { return(ParseBareIdentifier(context)); } } else { return(ParseBareIdentifier(context)); } } else { throw new KDLParseException(string.Format("Expected an identifier, but identifiers can't start with '{0}'", (char)c)); } }
private KDLDocument ParseDocument(KDLParseContext context, bool root) { var c = context.Peek(); if (c == EOF) { return(new KDLDocument(new List <KDLNode>(0))); } var nodes = new List <KDLNode>(); while (true) { bool skippingNode = false; switch (ConsumeWhitespaceAndLinespace(context)) { case WhitespaceResult.NodeSpace: case WhitespaceResult.NoWhitespace: break; case WhitespaceResult.EndNode: c = context.Peek(); if (c == EOF) { break; } else { continue; } case WhitespaceResult.SkipNext: skippingNode = true; break; } c = context.Peek(); if (c == EOF) { if (root) { return(new KDLDocument(nodes)); } else { throw new KDLParseException("Got EOF, expected a node or '}'"); } } else if (c == '}') { if (root) { throw new KDLParseException("Unexpected '}' in root document"); } else { return(new KDLDocument(nodes)); } } var node = ParseNode(context); ConsumeAfterNode(context); if (!skippingNode && node != null) { nodes.Add(node); } } }
public KDLDocument Parse(StreamReader reader) { var context = new KDLParseContext(reader); return(ParseDocument(context, true)); }
IKDLObject ParseArgOrProp(KDLParseContext context) // throws IOException { IKDLObject obj; bool isBare = false; int c = context.Peek(); if (c == '"') { obj = new KDLString(ParseEscapedString(context)); } else if (IsValidNumericStart(c)) { obj = ParseNumber(context); } else if (IsValidBareIdStart(c)) { string strVal; if (c == 'r') { context.Read(); int next = context.Peek(); context.Unread('r'); if (next == '"' || next == '#') { strVal = ParseRawString(context); } else { isBare = true; strVal = ParseBareIdentifier(context); } } else { isBare = true; strVal = ParseBareIdentifier(context); } if (isBare) { if ("true" == strVal) { obj = KDLBoolean.True; } else if ("false" == strVal) { obj = KDLBoolean.False; } else if ("null" == strVal) { obj = KDLNull.Instance; } else { obj = new KDLString(strVal); } } else { obj = new KDLString(strVal); } } else { throw new KDLParseException(string.Format("Unexpected character: '{0}'", (char)c)); } if (obj is KDLString kdlString) { c = context.Peek(); if (c == '=') { context.Read(); var value = ParseValue(context); return(new KDLProperty(kdlString.Value, value)); } else if (isBare) { throw new KDLParseException("Arguments may not be bare"); } else { return(obj); } } else { return(obj); } }
KDLNode?ParseNode(KDLParseContext context) // throws IOException { var args = new List <IKDLValue>(); var properties = new Dictionary <string, IKDLValue>(); KDLDocument?child = null; int c = context.Peek(); if (c == '}') { return(null); } var identifier = ParseIdentifier(context); while (true) { var whitespaceResult = ConsumeWhitespaceAndBlockComments(context); c = context.Peek(); switch (whitespaceResult) { case WhitespaceResult.NodeSpace: if (c == '{') { child = ParseChild(context); return(new KDLNode(identifier, properties, args, child)); } else if (IsUnicodeLinespace(c)) { return(new KDLNode(identifier, properties, args, child)); } if (c == EOF) { return(new KDLNode(identifier, properties, args, child)); } else { var obj = ParseArgOrProp(context); if (obj is IKDLValue kdlValue) { args.Add(kdlValue); } else if (obj is KDLProperty kdlProp) { properties[kdlProp.Key] = kdlProp.Value; } else { throw new KDLInternalException( string.Format("Unexpected type found, expected property, arg, or child: '{0}' type: {1}", obj.ToKDL(), obj.GetType().ToString())); } } break; case WhitespaceResult.NoWhitespace: if (c == '{') { child = ParseChild(context); return(new KDLNode(identifier, properties, args, child)); } else if (IsUnicodeLinespace(c) || c == EOF) { return(new KDLNode(identifier, properties, args, child)); } else if (c == ';') { context.Read(); return(new KDLNode(identifier, properties, args, child)); } else { throw new KDLParseException($"Unexpected character: '{(char)c}'"); } case WhitespaceResult.EndNode: return(new KDLNode(identifier, properties, args, child)); case WhitespaceResult.SkipNext: if (c == '{') { ParseChild(context); //Ignored return(new KDLNode(identifier, properties, args, child)); } else if (IsUnicodeLinespace(c)) { throw new KDLParseException("Unexpected skip marker before newline"); } else if (c == EOF) { throw new KDLParseException("Unexpected EOF following skip marker"); } else { var obj = ParseArgOrProp(context); if (!(obj is IKDLValue) && !(obj is KDLProperty)) { throw new KDLInternalException( string.Format("Unexpected type found, expected property, arg, or child: '{0}' type: {1}", obj.ToKDL(), obj.GetType())); } } break; } } }