// returns the part of the string remaining // will load into self, setting up everything. Assumes we're mpty/null to start. private string p_parseFromString(string str, ParseFlags parseFlags, Internal.ParserState parserState) { if (str.Length == 0) { throw new EmptyStringException(parserState.Line, parserState.Column, "Was told to parse an empty string"); } str = s_trimFrontOfString(str, parserState); if (str.Length == 0) { return(""); } // start parsing types: // if first two characters are #(, we're an array. // if @( we're a map. // if [] we're a ref. // if < we're a binary string // otherwise, we're a value. if (str.Length >= 2 && str.Substring(0, 2) == "#(") { // We're an array m_type = ExpressionType.Array; m_array.Init(); // move our string forward str = str.Substring(2); parserState.Column += 2; // continue building children as needed while (true) { str = s_trimFrontOfString(str, parserState); if (str.Length == 0) { throw new ArrayMissingEndParenException(parserState.Line, parserState.Column, "An Array was missing its ending paren"); } if (str.Substring(0, 1) == ")") // end array { break; // done } else { // parse as a new expression var newExpression = CreateNull(); str = newExpression.p_parseFromString(str, parseFlags, parserState); // add it to our array ArrayAddElementToEnd(newExpression); } } str = str.Substring(1); // remove the end array parserState.Column += 1; // done with array return(str); } else if (str.Length >= 2 && str.Substring(0, 2) == "@(") { // We're a map m_type = ExpressionType.Map; m_map.Init(); // move our string accordingly str = str.Substring(2); parserState.Column += 2; // build our children as needed while (true) { str = s_trimFrontOfString(str, parserState); if (str.Length == 0) { throw new MapMissingEndParenException(parserState.Line, parserState.Column, "A Map was missing its ending paren"); } if (str.Length >= 1 && str.Substring(0, 1) == ")") // end map { break; } else { // parse as a new expression - we'll alternate keys and values // keep our previous position just in case the value is bad. int prevLine = parserState.Line; int prevColumn = parserState.Column; var keyExpression = CreateNull(); str = keyExpression.p_parseFromString(str, parseFlags, parserState); if (keyExpression.ExpressionType != ExpressionType.Value) { throw new MapKeyMustBeAValueException(prevLine, prevColumn, "Map keys must be a value"); } var valueExpression = CreateInvalid(); try { str = valueExpression.p_parseFromString(str, parseFlags, parserState); } catch (EmptyStringException) { // we ignore ESEs because we want to consider that no value for a better error. // if not, the exception is good. } if (valueExpression.ExpressionType == ExpressionType.Invalid) { throw new MapNoValueException(prevLine, prevColumn, "Map key must have a value"); } // ok now we have the key and the value // both malloc so we can free later MapSetValueForKey(keyExpression.Value, valueExpression); } } // remove the end map str = str.Substring(1); parserState.Column += 1; // done with map return(str); } else if (str.Length >= 1 && str.Substring(0, 1) == "[") { // the current expression being processed is the one the attribute will be linked to. // process till the closing ] var endingBracketIndex = str.IndexOf(']'); if (endingBracketIndex == -1) { throw new ReferenceMissingEndBracketException(parserState.Line, parserState.Column, "A reference [] is missing its ending bracket"); } var refName = str.Substring(1, endingBracketIndex - 1); // validate the contents var invalidName = false; for (int i = 0; i < refName.Length; ++i) { char v = refName[i]; bool isAlpha = (v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z'); bool isNumber = (v >= '0' && v <= '9'); bool isUnder = (v == '_'); if (i == 0 && (isAlpha || isUnder)) { } else if (i != 0 && (isAlpha || isNumber || isUnder)) { } else { invalidName = true; break; } } if (invalidName) { throw new ReferenceInvalidNameException(parserState.Line, parserState.Column, "A reference doesn't have a valid name"); } // move forward parserState.MoveForwardBasedOnString(str.Substring(0, endingBracketIndex + 1)); str = str.Substring(endingBracketIndex + 1); // continue parsing at the same level : stored the reference name var resultString = p_parseFromString(str, parseFlags, parserState); // now bind the ref - creating a copy of what was made. This will be used for the template. parserState.InternalReferenceMap.SetExpressionForKey(refName, Copy()); // and continue return(resultString); } else if (str.Length >= 2 && str.Substring(0, 2) == "*[") { // parse the reference name var endingBracketIndex = str.IndexOf(']'); if (endingBracketIndex == -1) { throw new ReferenceInsertMissingEndBracketException(parserState.Line, parserState.Column, "A reference insert *[] is missing its ending bracket"); } var refName = str.Substring(2, endingBracketIndex - 2); // move forward parserState.MoveForwardBasedOnString(str.Substring(0, endingBracketIndex + 1)); str = str.Substring(endingBracketIndex + 1); var referenceExpr = parserState.InternalReferenceMap.ExpressionForKey(refName); if (referenceExpr == null) { // try again with the external if we have it if (parserState.ExternalReferenceMap != null) { referenceExpr = parserState.ExternalReferenceMap.ExpressionForKey(refName); } } if (referenceExpr == null) { // not found throw new ReferenceUnknownReferenceException(parserState.Line, parserState.Column, "Tried to insert a reference, but couldn't find it."); } // copy this into ourself p_copyFrom(referenceExpr); return(str); } // null expressions will be treated as a value, and then parsed seperately. else if ( str.Length >= 1 && str.Substring(0, 1) == "<" ) { // look for the ending < var endingQuote = str.IndexOf('>'); if (endingQuote == -1) { // not found throw new BinaryDataNoEndingException(parserState.Line, parserState.Column, "Tried to find the ending > for binary data, but not found."); } byte[] data; try { data = System.Convert.FromBase64String(str.Substring(1, endingQuote - 1)); // -1 for starting quote. ending was not part. } catch (Exception) { throw new BinaryDataInvalidBase64Exception(parserState.Line, parserState.Column, "Unable to decode the base64 data."); } m_type = ExpressionType.BinaryData; m_binaryData.data = data; parserState.MoveForwardBasedOnString(str.Substring(0, endingQuote + 1)); return(str.Substring(endingQuote + 1)); } else if (str.Length >= 1) // its a value: must be at least one character { var val = s_createValueOfString(str, parserState); // was it a null/nil string? if (val.value == "nil" || val.value == "null") { m_type = ExpressionType.Null; } else { m_type = ExpressionType.Value; m_value.data = val.value; } parserState.MoveForwardBasedOnString(str.Substring(0, val.endIndex)); return(str.Substring(val.endIndex)); } // otherwise we have no idea what happened return(""); }
// Trims the given string by removing whitespace or comments from the beginning of the string private static string s_trimFrontOfString(string str, Internal.ParserState parserState) { while (true) { if (str.Length == 0) // trimmed everything { return(str); } char first = str[0]; // skip whitespace if (s_isWhitespace(first)) { str = str.Substring(1); if (s_isNewline(first)) { parserState.Line += 1; parserState.Column = 1; } else { parserState.Column += 1; } } // comment else if (first == ';') { bool isTillNewline = true; if (str.Length >= 4) { if (str.Substring(0, 4) == s_StartBlockComment) { isTillNewline = false; } } int endIndex = (isTillNewline ? str.IndexOf('\n') // end of line : str.IndexOf(s_EndBlockComment) ); int lengthToSkip = isTillNewline ? 1 : s_EndBlockComment.Length; // Move forward columns/rows as needed parserState.MoveForwardBasedOnString( str.Substring(0, (endIndex == -1) ? str.Length : (endIndex + lengthToSkip)) ); if (endIndex == -1 || endIndex > str.Length - lengthToSkip) { str = ""; // dead } else // slice { str = str.Substring(endIndex + lengthToSkip); // skip the comment } } else { break; } } return(str); }