// ====================================================================================================
// YAML Parser for the .NET Framework
// ====================================================================================================
//
// Copyright (c) 2006
// Christophe Lambrechts
// Jonathan Slenders
//
// ====================================================================================================
// This file is part of the .NET YAML Parser.
//
// This .NET YAML parser is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// The .NET YAML parser is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Foobar; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USAusing System.Reflection;
// ====================================================================================================
using System;
// Unicode support:
// http://www.yoda.arachsys.com/csharp/unicode.html
namespace Yaml
{
///
/// Yaml String node
///
public class String : Scalar
{
private string content;
private bool block = false;
private bool folded = false;
/// New string constructor
public String (string val) :
base ("tag:yaml.org,2002:str", NodeType.String)
{
content = val;
}
/// Parse a string
public String (ParseStream stream) :
base ("tag:yaml.org,2002:str", NodeType.String)
{
// set flags for folded or block scalar
if (stream.Char == '>') // TODO: '+' and '-' chomp chars
folded = true;
else if (stream.Char == '|')
block = true;
if (block || folded)
{
stream.Next ();
stream.SkipSpaces ();
}
// -----------------
// Folded Scalar
// -----------------
if (folded)
{
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
// First line (the \n after the first line is always ignored,
// not replaced with a whitespace)
while (! stream.EOF && stream.Char != '\n')
{
builder.Append (stream.Char);
stream.Next ();
}
// Skip the first newline
stream.Next ();
// Next lines (newlines will be replaced by spaces in folded scalars)
while (! stream.EOF)
{
if (stream.Char == '\n')
builder.Append (' ');
else
builder.Append (stream.Char);
stream.Next (true);
}
content = builder.ToString ();
}
// -----------------
// Block Scalar (verbatim block without folding)
// -----------------
else if (block)
{
/*
Console.Write(">>");
while (! stream.EOF)
{
Console.Write (stream.Char);
stream.Next();
}
Console.Write("<<");
// */
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
while (! stream.EOF)
{
builder.Append (stream.Char);
stream.Next (true);
}
content = builder.ToString ();
}
// String between double quotes
if (stream.Char == '\"')
content = ParseDoubleQuoted (stream);
// Single quoted string
else if (stream.Char == '\'')
content = ParseSingleQuoted (stream);
// String without quotes
else
content = ParseUnQuoted (stream);
}
///
/// Parses a String surrounded with single quotes
///
private string ParseSingleQuoted (ParseStream stream)
{
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
// Start literal parsing
stream.StartLiteral ();
// Skip '''
stream.Next (true);
while (! stream.EOF)
{
if (stream.Char == '\'')
{
stream.Next ();
// Escaped single quote
if (stream.Char == '\'')
builder.Append (stream.Char);
// End of string
else
break;
}
else
builder.Append (stream.Char);
stream.Next ();
// Skip \'
if (stream.EOF)
{
stream.StopLiteral ();
throw new ParseException (stream,
"Single quoted string not closed");
}
}
// Stop literal parsing
stream.StopLiteral ();
return builder.ToString();
}
///
/// Parses a String surrounded with double quotes
///
private string ParseDoubleQuoted(ParseStream stream)
{
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
// Skip '"'
stream.Next ();
// Stop at "
stream.StopAt (new char [] {'\"'} );
while (! stream.EOF)
{
if (stream.Char == '\n')
{
builder.Append (' ');
stream.Next ();
}
else
builder.Append (NextUnescapedChar (stream));
}
// Don't stop at "
stream.DontStop ();
// Skip '"'
if (stream.Char != '\"')
throw new ParseException (stream,
"Double quoted string not closed");
else
stream.Next (true);
return builder.ToString();
}
///
/// Parses a String surrounded without nothing
///
private string ParseUnQuoted(ParseStream stream)
{
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
while (! stream.EOF)
builder.Append (NextUnescapedChar (stream));
// Trimming left
int count = 0;
while (count < builder.Length &&
(builder [count] == ' ' || builder [count] == '\t'))
count ++;
if (count >= 0)
builder.Remove (0, count);
// Trimming right
count = 0;
while (count < builder.Length &&
(builder [builder.Length - count - 1] == ' ' ||
builder [builder.Length - count - 1] == '\t'))
count ++;
if (count >= 0)
builder.Remove (builder.Length - count, count);
return builder.ToString();
}
///
/// Reads a character from the stream, unescapes it,
/// and moves to the next character.
///
private char NextUnescapedChar (ParseStream stream)
{
char c = stream.Char;
// If escaped
if (c == '\\')
{
// Never stop, every special character
// looses its meaning behind a backslash.
stream.StopAt (new Char [] { });
stream.Next (true);
c = stream.Char;
// ASCII null
if (c == '0') c = '\0';
// ASCII bell
else if (c == 'a') c = (char) 0x7;
// ASCII backspace
else if (c == 'b') c = (char) 0x8;
// ASCII horizontal tab
else if (c == 't') c = (char) 0x9;
// ASCII newline
else if (c == 'n') c = (char) 0xA;
// ASCII vertical tab
else if (c == 'v') c = (char) 0xB;
// ASCII form feed
else if (c == 'f') c = (char) 0xC;
// ASCII carriage return
else if (c == 'r') c = (char) 0xD;
// ASCII escape
else if (c == 'e') c = (char) 0x1D;
// Unicode next line
else if (c == 'N') c = (char) 0x85;
// Unicode non breaking space
else if (c == '_') c = (char) 0xA0;
// TODO larger unicode characters
// Unicode line separator
// else if (c == 'L') c = (char) 0x20282028;
// 8 bit hexadecimal
else if (c == 'x')
{
int c_int = (char) 0;
for (int i = 0; i < 2; i ++)
{
c_int *= 16;
stream.Next ();
char d = stream.Char;
if (d >= '0' && d <= '9')
c_int += d - '0';
else if (d >= 'a' && d <= 'f')
c_int += d - 'a';
else if (d >= 'A' && d <= 'F')
c_int += d - 'A';
else
{
stream.DontStop ();
throw new ParseException (stream,
"Invalid escape sequence");
}
}
c = (char) c_int;
}
stream.Next (true);
// Restore last stop settings
stream.DontStop ();
}
else
stream.Next (true);
return c;
}
/// Content property
public string Content
{
get { return content; }
set { content = value; }
}
/// To String
public override string ToString ()
{
return "[STRING]" + content + "[/STRING]";
}
/// Write
protected internal override void Write (WriteStream stream)
{
// TODO, not required, but writing to block or folded scalars
// generates a little more neat code.
// Analyze string
bool multiline = false;
bool mustbequoted = false;
for (int i = 0; i < content.Length; i ++)
{
char c = content [i];
if (c == '\n')
multiline = true;
// We quote everything except strings like /[a-zA-Z]*/
// However there are more strings which don't require
// quotes.
if ( ! ( c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'))
mustbequoted = true;
}
// Double quoted strings
if (mustbequoted)
{
stream.Append ("\"");
for (int i = 0; i < content.Length; i ++)
{
char c = content [i];
// Backslash
if (c == '\\') stream.Append ("\\" + "\\");
// Double quote
else if (c == '\"') stream.Append ("\\" + "\"");
// Single quote
else if (c == '\'') stream.Append ("\\" + "\'");
// ASCII null
else if (c == '\0') stream.Append ("\\0");
// ASCII bell
else if (c == (char) 0x7) stream.Append ("\\a");
// ASCII backspace
else if (c == (char) 0x8) stream.Append ("\\b");
// ASCII horizontal tab
else if (c == (char) 0x9) stream.Append ("\\t");
// ASCII newline
else if (c == (char) 0xA) stream.Append ("\\n");
// ASCII vertical tab
else if (c == (char) 0xB) stream.Append ("\\v");
// ASCII form feed
else if (c == (char) 0xC) stream.Append ("\\f");
// ASCII carriage return
else if (c == (char) 0xD) stream.Append ("\\r");
// ASCII escape
else if (c == (char) 0x1D) stream.Append ("\\e");
// Unicode next line
else if (c == (char) 0x85) stream.Append ("\\N");
// Unicode non breaking space
else if (c == (char) 0xA0) stream.Append ("\\_");
// TODO larger unicode characters
else
stream.Append ("" + c);
}
stream.Append ("\"");
}
// Simple non-quoted strings
else
stream.Append (content);
}
}
}