{- YAML Grammar -}
%Namespace = QiHe.Yaml.Grammar;
%ParserName = YamlParser;

{- Yaml Document -}

	YamlStream
		<- Comment* Documents:(ImplicitDocument? ExplicitDocument*) <end>;

	YamlDocument ImplicitDocument
		<- {currentDocument = yamlDocument; currentIndent = -1;}
		   Root:IndentedBlockNode EndOfDocument?;

	YamlDocument ExplicitDocument
		<- {currentDocument = yamlDocument; currentIndent = -1;}
		   Directives:Directive* '---' Root:SeparatedBlockNode EndOfDocument?;
		
	void EndOfDocument
		<- '...' InlineComments;

{- Directives -}

	Directive
		<- YamlDirective
		 / TagDirective
		 / ReservedDirective
		 ;

	ReservedDirective
		<- '%' Name:DirectiveName Parameters:(SeparationSpace $DirectiveParameter)* InlineComments;

		string DirectiveName
			<- $NonSpaceChar+;

		string DirectiveParameter
			<- $NonSpaceChar+;

	YamlDirective
		<- 'YAML' SeparationSpace Version:YamlVersion InlineComments;

		YamlVersion
			<- Major:Integer '.' Minor:Integer;

	TagDirective
		<- 'TAG' SeparationSpace Handle:TagHandle SeparationSpace Prefix:TagPrefix InlineComments;

		TagHandle
			<- NamedTagHandle
			 / SecondaryTagHandle
			 / PrimaryTagHandle
			 ;

			PrimaryTagHandle
				<- '!';

			SecondaryTagHandle
				<- '!!';

			NamedTagHandle
				<- '!' Name:WordChar+ '!';

		TagPrefix
			<- LocalTagPrefix
			 / GlobalTagPrefix
			 ;

			LocalTagPrefix
				<- '!' Prefix:UriChar*;

			GlobalTagPrefix
				<- Prefix:UriChar+;

{- Inheritance relation -}

	DataItem  {- Just for generate type inheritance -}
		<- (Property:NodeProperty SeparationLines)? (Scalar / Sequence / Mapping);

	Scalar
		<- FlowScalarInBlock / FlowScalarInFlow / BlockScalar;

	Sequence
		<- FlowSequence / BlockSequence;

	Mapping
		<- FlowMapping / BlockMapping;

{- Node structure -}

	DataItem IndentedBlockNode
		<- { IncreaseIndent(); } IndentedBlock @{ DecreaseIndent(); };
		
	DataItem SeparatedBlockNode
		<- { IncreaseIndent(); } SeparatedBlock @{ DecreaseIndent(); };

	DataItem IndentedBlock
		<- IndentedContent
		 / Indent AliasNode InlineComments
		 / Indent property:NodeProperty SeparatedContent? { SetDataItemProperty(dataItem, property); }
		 ;
		 
	DataItem SeparatedBlock
		<- SeparatedContent
		 / SeparationLines AliasNode InlineComments
		 / SeparationSpace property:NodeProperty SeparatedContent?{ SetDataItemProperty(dataItem, property); }
		 / EmptyBlock
		 ;

	DataItem IndentedContent
		<- Indent BlockContent
		 / Indent FlowContentInBlock InlineComments
		 ;
		 
	DataItem SeparatedContent
		<- InlineComments IndentedContent
		 / SeparationSpace BlockScalar
		 / SeparationSpace FlowContentInBlock InlineComments
		 ;
		 
	DataItem BlockCollectionEntry
		<- { IncreaseIndent(); } (SeparationSpaceAsIndent BlockCollection) @{ DecreaseIndent(); }
		 / SeparatedBlockNode
		 ;
		 
	DataItem BlockCollectionEntryOptionalIndent
		<- { RememberIndent(); } (SeparationSpaceAsIndent BlockCollection) @{ RestoreIndent(); }
		  / { RememberIndent(); } SeparatedBlock @{ RestoreIndent(); }
		  ;

	DataItem FlowNodeInFlow
		<- AliasNode 
		 / FlowContentInFlow
		 / property:NodeProperty {dataItem = new Scalar();} (SeparationLinesInFlow FlowContentInFlow)? { SetDataItemProperty(dataItem, property); }
		 ;

	DataItem AliasNode 
		<- '*' name:AnchorName { return GetAnchoredDataItem(name); };

	DataItem FlowContentInBlock
		<- FlowScalarInBlock
		 / FlowSequence
		 / FlowMapping;

	DataItem FlowContentInFlow
		<- FlowScalarInFlow
		 / FlowSequence
		 / FlowMapping;

	DataItem BlockContent
		<- BlockScalar
		 / BlockSequence
		 / BlockMapping;

	DataItem BlockCollection
		<- BlockSequence
		 / BlockMapping;

	DataItem EmptyFlow <- <empty> { return new Scalar(); };
	DataItem EmptyBlock <- EmptyFlow InlineComments;

{- Node Property -}

	NodeProperty <- Tag:Tag (SeparationLines Anchor:Anchor)? / Anchor:Anchor (SeparationLines Tag:Tag)?;

	string Anchor
		<- '&' $AnchorName;

	string AnchorName
		<- $NonSpaceChar+;

	Tag
		<- VerbatimTag
		 / ShorthandTag
		 / NonSpecificTag
		 ;

	NonSpecificTag
		<- '!';

	VerbatimTag
		<- '!' '<' Chars:UriChar+ '>';

	ShorthandTag
		<- NamedTagHandle Chars:TagChar+
		 / SecondaryTagHandle Chars:TagChar+
		 / PrimaryTagHandle Chars:TagChar+
		 ;

{- Scalar -}

	Scalar FlowScalarInBlock
		<- Text:PlainTextMultiLine / Text:SingleQuotedText / Text:DoubleQuotedText;

	Scalar FlowScalarInFlow
		<- Text:PlainTextInFlow / Text:SingleQuotedText / Text:DoubleQuotedText;

	Scalar BlockScalar
		<- Text:LiteralText / Text:FoldedText;

	{- Plain Text -}

		string PlainText <- $PlainTextMultiLine / $PlainTextInFlow;

		string PlainTextMultiLine <- $PlainTextSingleLine $PlainTextMoreLine*;

		string PlainTextSingleLine <- !DocumentMarker $PlainTextFirstChar $(PlainTextChar / SpacedPlainTextChar)*;

		string PlainTextMoreLine <- IgnoredBlank $LineFolding Indent IgnoredSpace $(PlainTextChar / SpacedPlainTextChar)+;

		string PlainTextInFlow <- $PlainTextInFlowSingleLine $PlainTextInFlowMoreLine*;

		string PlainTextInFlowSingleLine <- !DocumentMarker $PlainTextFirstCharInFlow $(PlainTextCharInFlow / SpacedPlainTextCharInFlow)*;

		string PlainTextInFlowMoreLine <- IgnoredBlank $LineFolding Indent IgnoredSpace $(PlainTextCharInFlow / SpacedPlainTextCharInFlow)+;

		string PlainTextFirstChar <- $-"\r\n\t -?:,[]{}#&*!|>'\"%@`" / $"-?:" $NonSpaceChar;

		string PlainTextChar <- $':' $NonSpaceChar / $NonSpaceChar $'#'+ / { text.Length = 0; } ch2:-"\r\n\t :#";
		
		string SpacedPlainTextChar <- $' '+ $PlainTextChar;

		string PlainTextFirstCharInFlow <- $-"\r\n\t -?:,[]{}#&*!|>'\"%@`" 
		                          / $"-?:" $NonSpaceSep;

		string PlainTextCharInFlow <- $":" $NonSpaceSep / $NonSpaceSep $'#' / { text.Length = 0; } ch2:-"\r\n\t :#,[]{}";
		
		string SpacedPlainTextCharInFlow <- $' '+ $PlainTextCharInFlow;

		void DocumentMarker <- <sol> '---' (Space/LineBreak) 
                             / <sol> '...' (Space/LineBreak);
   
	{- Quoted Text -}

		string DoubleQuotedText <- $DoubleQuotedSingleLine / $DoubleQuotedMultiLine;

		string DoubleQuotedSingleLine <- '"' $(-"\"\\\r\n" / EscapeSequence)* '"';

		string DoubleQuotedMultiLine <- $DoubleQuotedMultiLineFist $DoubleQuotedMultiLineInner* $DoubleQuotedMultiLineLast;

		string DoubleQuotedMultiLineFist <- '"' $(-" \"\\\r\n" / EscapeSequence / ' ' !(IgnoredBlank LineBreak))* IgnoredBlank $DoubleQuotedMultiLineBreak;

		string DoubleQuotedMultiLineInner <- Indent IgnoredBlank $(-" \"\\\r\n" / EscapeSequence / ' ' !(IgnoredBlank LineBreak))+  IgnoredBlank $DoubleQuotedMultiLineBreak;

		string DoubleQuotedMultiLineLast <- Indent IgnoredBlank $(-"\"\\\r\n" / EscapeSequence)* '"';

		string DoubleQuotedMultiLineBreak <- $LineFolding / EscapedLineBreak;

		string SingleQuotedText <- $SingleQuotedSingleLine / $SingleQuotedMultiLine;

		string SingleQuotedSingleLine <- '\'' $(-"'\r\n" / EscapedSingleQuote)* '\'';

		string SingleQuotedMultiLine <- $SingleQuotedMultiLineFist $SingleQuotedMultiLineInner* $SingleQuotedMultiLineLast;

		string SingleQuotedMultiLineFist <- '\'' $(-" '\r\n" / EscapedSingleQuote / ' ' !(IgnoredBlank LineBreak))* IgnoredBlank fold:LineFolding;

		string SingleQuotedMultiLineInner <- Indent IgnoredBlank $(-" '\r\n" / EscapedSingleQuote / ' ' !(IgnoredBlank LineBreak))+  IgnoredBlank fold:LineFolding;

		string SingleQuotedMultiLineLast <- Indent IgnoredBlank $(-"'\r\n" / EscapedSingleQuote)* '\'';

		string LineFolding <- $ReservedLineBreak (IgnoredBlank LineBreak)+ &Indent / LineBreak &Indent {return " ";};
		
		char EscapedSingleQuote <- { char ch = default(char); } '\'\'' { return '\''; };

		void EscapedLineBreak <- '\\' LineBreak (IgnoredBlank LineBreak)*;

	{- Block Text -}

		string LiteralText <- '|' (modifier:BlockScalarModifier)? @{AddIndent(modifier, success);} InlineComment $LiteralContent? @{DecreaseIndent();};

		string FoldedText <- '>' (modifier:BlockScalarModifier)? @{AddIndent(modifier, success);} InlineComment ((str_2:$EmptyLineBlock)* $FoldedLines  $ChompedLineBreak Comments?) @{DecreaseIndent();};

		BlockScalarModifier <- Indent:IndentIndicator (Chomp:ChompingIndicator)? / Chomp:ChompingIndicator (Indent:IndentIndicator)?;
		
		string LiteralContent <- $LiteralFirst $LiteralInner* str2:ChompedLineBreak Comments?;
		string LiteralFirst <- $EmptyLineBlock* Indent $NonBreakChar+;
		string LiteralInner <- $ReservedLineBreak $EmptyLineBlock* Indent $NonBreakChar+;

		string FoldedLine <- Indent $NonBreakChar*;
		string FoldedLines <-  str2:FoldedLine $(LineFolding FoldedLine)*;

		string SpacedLine <- Indent Blank $NonBreakChar*;
		string SpacedLines <- str2:SpacedLine (LineBreak $SpacedLine)*;

		char IndentIndicator <- $'1'...'9';

		char ChompingIndicator <- $'-' / $'+';

{- Sequence -}

	Sequence FlowSequence
		<- '[' SeparationLinesInFlow? Enties:(FlowSequenceEntry (',' SeparationLinesInFlow? FlowSequenceEntry)*) ']';

	DataItem FlowSequenceEntry
		<- FlowNodeInFlow SeparationLinesInFlow?
		 / FlowSingPair
		 ;

	Sequence BlockSequence
		<- Enties:(BlockSequenceEntry (Indent BlockSequenceEntry)*);

	DataItem BlockSequenceEntry
		<- '-' BlockCollectionEntry;

{- Mapping -}

	Mapping FlowMapping
		<- '{' SeparationLinesInFlow? Enties:(FlowMappingEntry (',' SeparationLinesInFlow? FlowMappingEntry)*) '}';

	MappingEntry FlowMappingEntry
		<- Key:ExplicitKey Value:ExplicitValue
		 / Key:ExplicitKey Value:EmptyFlow
		 / Key:SimpleKey Value:ExplicitValue
		 / Key:SimpleKey Value:EmptyFlow
		 ;

	DataItem ExplicitKey
		<- '?' SeparationLinesInFlow FlowNodeInFlow SeparationLinesInFlow?
		 / '?' EmptyFlow SeparationLinesInFlow
		 ;

	DataItem SimpleKey
		<- FlowKey SeparationLinesInFlow?;

	Scalar FlowKey 
		<- Text:PlainTextInFlowSingleLine
		 / Text:DoubleQuotedSingleLine
		 / Text:SingleQuotedSingleLine
		 ;

	Scalar BlockKey 
		<- Text:PlainTextSingleLine
		 / Text:DoubleQuotedSingleLine
		 / Text:SingleQuotedSingleLine
		 ;

	DataItem ExplicitValue
		<- ':' SeparationLinesInFlow FlowNodeInFlow SeparationLinesInFlow?
		 / ':' EmptyFlow SeparationLinesInFlow
		 ;

	MappingEntry FlowSingPair
		<- Key:ExplicitKey Value:ExplicitValue
		 / Key:ExplicitKey Value:EmptyFlow
		 / Key:SimpleKey Value:ExplicitValue
		 ;

	Mapping BlockMapping
		<- Enties:(BlockMappingEntry (Indent BlockMappingEntry)*);

	MappingEntry BlockMappingEntry
		<- Key:BlockExplicitKey Value:BlockExplicitValue
		 / Key:BlockExplicitKey Value:EmptyFlow
		 / Key:BlockSimpleKey Value:BlockSimpleValue
		 / Key:BlockSimpleKey Value:EmptyBlock
		 ;

	DataItem BlockExplicitKey
		<- '?' BlockCollectionEntry;

	DataItem BlockExplicitValue
		<- Indent ':' BlockCollectionEntry;

	DataItem BlockSimpleKey
		<- BlockKey SeparationLines? ':';

	DataItem BlockSimpleValue
		<- BlockCollectionEntry;

{- Comment -}

	void Comment
		<- !<eof> IgnoredSpace ('#' NonBreakChar*)? (LineBreak / <eof>);

	void InlineComment
		<- (SeparationSpace ('#' NonBreakChar*)?)? (LineBreak / <eof>);

	void Comments
		<- Comment+;

	void InlineComments
		<- InlineComment Comment*;


string Integer <- chars:Digit+ { return new string(chars.ToArray()); };

char WordChar <- $Letter / $Digit / $'-';

char Letter <- $'a'...'z' / $'A'...'Z';
	 
char Digit <- $'0'...'9';

char HexDigit <- $'0'...'9' / $'A'...'F' / $'a'...'f';

char UriChar
	<- $WordChar 
	 / '%' char1:HexDigit char2:HexDigit { ch = Convert.ToChar(int.Parse(String.Format("{0}{1}", char1, char2), System.Globalization.NumberStyles.HexNumber));} 
	 / ";/?:@&=+$,_.!~*'()[]";

char TagChar
	<- $WordChar 
	 / '%' char1:HexDigit char2:HexDigit { ch = Convert.ToChar(int.Parse(String.Format("{0}{1}", char1, char2), System.Globalization.NumberStyles.HexNumber));} 
	 / ";/?:@&=+$,_.~*'()[]";

void EmptyLinePlain <- IgnoredSpace NormalizedLineBreak;

void EmptyLineQuoted <- IgnoredBlank NormalizedLineBreak;

string EmptyLineBlock <- IgnoredSpace $ReservedLineBreak;

char NonSpaceChar <- -" \t\r\n";
char NonSpaceSep <-  -"\r\n\t ,[]{}";

char NonBreakChar <- -"\r\n";

void IgnoredSpace <- ' '*;
void IgnoredBlank <- " \t"*;
void SeparationSpace <- ' '+;
void SeparationLines <- InlineComments Indent / SeparationSpace;
void SeparationLinesInFlow <- InlineComments {detectIndent = false;} Indent IgnoredSpace / SeparationSpace;

void SeparationSpaceAsIndent
	 <- (' ' { currentIndent++; })+;

void Indent <- { success = ParseIndent(); };

char Space <- ' ';
char Blank <- " \t";
void LineBreak <- '\r\n' / '\r' / '\n';
string ReservedLineBreak <- $'\r\n' / $'\r' / $'\n';
string ChompedLineBreak <-  ($ReservedLineBreak (IgnoredSpace $ReservedLineBreak)* / <eof>) @{ return Chomp(text.ToString()); };

char NormalizedLineBreak <- { char ch = default(char); } LineBreak { return '\n'; };

char EscapeSequence 
        <- '\\\\' { return '\\'; }
         / '\\\'' { return '\''; }
         / '\\"'  { return '\"'; }
         / '\\r'  { return '\r'; }
         / '\\n'  { return '\n'; }
         / '\\t'  { return '\t'; }
         / '\\v'  { return '\v'; }
         / '\\a'  { return '\a'; }
         / '\\b'  { return '\b'; }
         / '\\f'  { return '\f'; }
         / '\\0'  { return '\0'; }
         / '\\/'  { return '/'; }
         / '\\ '  { return ' '; }
         / '\\	'  { return '	'; }
         / '\\_'  { return '\u00A0'; }
         / '\\e'  { return '\u001B'; }
         / '\\N'  { return '\u0085'; }
         / '\\L'  { return '\u2028'; }
         / '\\P'  { return '\u2029'; }
	 / '\\x' char1:HexDigit char2:HexDigit { return Convert.ToChar(int.Parse(String.Format("{0}{1}", char1, char2), System.Globalization.NumberStyles.HexNumber));}
	 / '\\u' char1:HexDigit char2:HexDigit char3:HexDigit char4:HexDigit { return Convert.ToChar(int.Parse(String.Format("{0}{1}{2}{3}", char1, char2, char3, char4), System.Globalization.NumberStyles.HexNumber));}
	 ;