예제 #1
0
파일: Entity.cs 프로젝트: GlenDC/L20n.cs
				public static void Parse(
					CharStream stream, string identifier,
					Internal.LocaleContext.Builder builder)
				{
					var startingPos = stream.Position;

					try
					{
						// private identifiers start with an underscore
						// and can only be referenced from within an l20n file
						bool isPrivate = (identifier.IndexOf('_') == 0);

						// an optional index is possible
						AST.INode index = null;
						Index.PeekAndParse(stream, out index);

						// White Space is required
						WhiteSpace.Parse(stream, false);

						var valuePos = stream.Position;

						// Now we need the actual value
						var value = Value.Parse(stream);

						if ((value as IO.AST.HashValue) == null && index != null)
						{
							string msg = String.Format(
								"an index was given, but a stringValue was given, while a hashValue was expected",
								stream.ComputeDetailedPosition(valuePos));
							throw new Exceptions.ParseException(msg);
						}

						// an optional attributes collection is possible
						AST.Attributes attributes;
						Attributes.PeekAndParse(stream, out attributes);

						// White Space is optional
						WhiteSpace.Parse(stream, true);

						stream.SkipCharacter('>');
						
						var entityAST = new AST.Entity(identifier, isPrivate, index, value, attributes);
						try
						{
							var entity = (Objects.Entity)entityAST.Eval();
							builder.AddEntity(identifier, entity);
						} catch (Exception e)
						{
							throw new Exceptions.EvaluateException(
								String.Format("couldn't evaluate `{0}`", entityAST.Display()),
								e);
						}
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing an <entity> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #2
0
파일: Quote.cs 프로젝트: GlenDC/L20n.cs
				public static Info Parse(CharStream stream, Info expected = null)
				{
					string output;
					int pos = stream.Position;

					if (!stream.ReadReg("(\'|\")", out output))
					{
						throw new Exceptions.ParseException(
							String.Format(
							"expected to read a <quote> (starting at {0}), but no characters were left",
							stream.ComputeDetailedPosition(pos)));
					}

					Info info;
					if (output[0] == '"')
					{
						info = new Info(Type.Double);
					} else 
					{
						info = new Info(Type.Single);
					}

					if (expected != null && expected.CompareTo(info) != 0)
					{
						throw new Exceptions.ParseException(
							String.Format(
							"expected to read {0} (starting at {1}), but found {2}",
							expected.ToString(),
							stream.ComputeDetailedPosition(pos),
							info.ToString()));
					}

					return info;
				}
예제 #3
0
파일: Logic.cs 프로젝트: GlenDC/L20n.cs
					public static AST.INode Parse(CharStream stream)
					{
						var startingPos = stream.Position;

						try
						{
							var first = Binary.Parse(stream);
							string op;
							if (stream.ReadReg(@"\s*(\|\||\&\&)", out op))
							{
								WhiteSpace.Parse(stream, true);
								var second = Logic.Parse(stream);
								return new AST.LogicExpression(
									first, second, op.Trim());
							} else
							{
								return first;
							}
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <logical_expression> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #4
0
파일: Index.cs 프로젝트: GlenDC/L20n.cs
				public static AST.INode Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{
						// skip open char
						stream.SkipCharacter('[');

						// we need at least one index
						var index = new AST.Index(ParseExpression(stream));

						// others are optional
						while (stream.SkipIfPossible(','))
						{
							index.AddIndex(ParseExpression(stream));
						}

						// skip close char
						stream.SkipCharacter(']');

						return index;
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing an <index> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #5
0
파일: Expander.cs 프로젝트: GlenDC/L20n.cs
				public static AST.INode Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					AST.INode expression = null;

					try
					{
						// skip opening tags
						stream.SkipString("{{");
						WhiteSpace.Parse(stream, true);

						// parse actual expression
						expression = Expression.Parse(stream);

						// skip closing tags
						WhiteSpace.Parse(stream, true);
						stream.SkipString("}}");

						return expression;
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing an <expander> starting at {0}, expression was: {1} ({2})",
							stream.ComputeDetailedPosition(startingPos),
							expression != null ? expression.Display() : "<null>",
							expression != null ? expression.GetType().ToString() : "null");
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #6
0
				public static AST.INode Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{
						var condition = Expressions.Logic.Parse(stream);

						// check if we have an IfElse case or simply a logical expression
						string s;
						if (stream.ReadReg(@"\s*\?\s*", out s))
						{
							var first = Expression.Parse(stream);
							WhiteSpace.Parse(stream, true);
							stream.SkipCharacter(':');
							WhiteSpace.Parse(stream, true);
							var second = Expression.Parse(stream);
							return new AST.Conditional(condition, first, second);
						} else
						{ // it's simply a logical expression
							return condition;
						}
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing an <expression> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #7
0
				public static string Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{
						var output = "";
						var quote = Quote.Parse(stream);

						char c;
						
						while ((c = stream.PeekNext()) != '\0')
						{
							if (c == '\\')
							{
								output += stream.ForceReadNext();
							} else if (Quote.Peek(stream, quote))
							{
								break; // un-escaped quote means we're ending the string
							}

							output += stream.ForceReadNext();
						}
						
						Quote.Parse(stream, quote);
						
						return output;
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing an <string_value> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #8
0
파일: HashItem.cs 프로젝트: GlenDC/L20n.cs
				public static AST.HashValue.Item Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{
						// check if a hash item is supposed to be a default
						bool isDefault = stream.SkipIfPossible('*');
						
						// parse the raw identifier (key)
						var identifier = Identifier.Parse(stream, false);
						
						// whitespace is optional
						WhiteSpace.Parse(stream, true);
						// the seperator char is required as it seperates the key and the value
						stream.SkipCharacter(':');
						// more optional whitespace
						WhiteSpace.Parse(stream, true);
						
						// get the actual value, which is identified by the key
						var value = Value.Parse(stream);
						
						return new AST.HashValue.Item(identifier, value, isDefault);
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing a <hash_item> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #9
0
파일: Macro.cs 프로젝트: GlenDC/L20n.cs
				public static void Parse(
					CharStream stream,
					string identifier, Internal.LocaleContext.Builder builder)
				{
					var startingPos = stream.Position;
					
					try
					{
						var macroAST = new AST.Macro(identifier);
						
						stream.SkipCharacter('(');
						WhiteSpace.Parse(stream, true);

						// variables are optional,
						// but we do have them, we need at least one (duh)
						if (Expressions.Variable.Peek(stream))
						{
							macroAST.AddParameter(Macro.ParseVariable(stream));

							// more than 1 is possible as well
							while (stream.SkipIfPossible(','))
							{
								WhiteSpace.Parse(stream, true);
								macroAST.AddParameter(Macro.ParseVariable(stream));
							}
						}

						stream.SkipCharacter(')');
						WhiteSpace.Parse(stream, false);

						stream.SkipCharacter('{');
						WhiteSpace.Parse(stream, true);

						// Parse the Actual Macro Expression
						macroAST.SetExpression(Expression.Parse(stream));

						WhiteSpace.Parse(stream, true);
						stream.SkipCharacter('}');
						WhiteSpace.Parse(stream, true);
						stream.SkipCharacter('>');

						// return the fully parsed macro
						try
						{
							var macro = (Objects.Macro)macroAST.Eval();
							builder.AddMacro(identifier, macro);
						} catch (Exception e)
						{
							throw new Exceptions.EvaluateException(
								String.Format("couldn't evaluate `{0}`", macroAST.Display()),
								e);
						}
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing a <macro> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #10
0
				public static AST.Attributes.Item Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{	
						WhiteSpace.Parse(stream, false);

						// parse the raw identifier (key)
						var identifier = Identifier.Parse(stream, false);

						// parse the index if possible
						AST.INode index;
						Index.PeekAndParse(stream, out index);

						WhiteSpace.Parse(stream, true);
						// required seperator
						stream.SkipCharacter(':');

						WhiteSpace.Parse(stream, true);

						var valuePos = stream.Position;

						// the actual value (StringValue or HashTable)
						var value = Value.Parse(stream);

						if ((value as IO.AST.HashValue) == null && index != null)
						{
							string msg = String.Format(
								"an index was given, but a stringValue was given, while a hashValue was expected",
								stream.ComputeDetailedPosition(valuePos));
							throw new Exceptions.ParseException(msg);
						}
						
						return new AST.Attributes.Item(identifier, index, value);
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing a <key_value_pair> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #11
0
파일: Attribute.cs 프로젝트: GlenDC/L20n.cs
					public static AST.INode Parse(CharStream stream)
					{
						var startingPos = stream.Position;
						
						try
						{
							AST.INode root;
							if(!Property.PeekAndParse(stream, out root)) {
								root = IdentifierExpression.Parse(stream);
							}
							stream.SkipString("::");

							// we either have an expression or a simple identifier
							AST.INode identifier;
							if (stream.SkipIfPossible('['))
							{
								identifier = Expression.Parse(stream);
								stream.SkipCharacter(']');
							} else
							{
								identifier = new AST.Identifier(Identifier.Parse(stream, false));
							}

							// We can also have optionally a property expression,
							// starting with a simple identifier or straight away with an expression
							AST.PropertyExpression propertyExpression = null;
							if (stream.SkipIfPossible('.'))
							{
								propertyExpression = Property.Parse(stream) as AST.PropertyExpression;
							}
							else if (stream.SkipIfPossible('['))
							{
								var expression = Expression.Parse(stream);
								propertyExpression = new AST.PropertyExpression(expression);
								stream.SkipCharacter(']');
								Property.Parse(stream, propertyExpression);
							}

							return new AST.AttributeExpression(root, identifier, propertyExpression);
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <property_expression> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #12
0
				public static void Parse(CharStream stream, Internal.LocaleContext.Builder builder)
				{
					var startingPos = stream.Position;
					
					try
					{
						stream.SkipString("import(");
						WhiteSpace.Parse(stream, true);
						var path = PureStringValue.Parse(stream);
						WhiteSpace.Parse(stream, true);
						stream.SkipCharacter(')');

						LocalizableObjectsList.ImportAndParse(path, builder);
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing an <import_statement> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #13
0
				public static AST.Attributes Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{
						var attributes = new AST.Attributes();
						AST.Attributes.Item item;
						while (KeyValuePair.PeekAndParse(stream, out item))
						{
							attributes.AddItem(item);
						}
						
						return attributes;
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing a <attributes> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #14
0
파일: Value.cs 프로젝트: GlenDC/L20n.cs
				public static AST.INode Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{
						AST.INode value;
						if (!Value.PeekAndParse(stream, out value))
						{
							throw new Exceptions.ParseException(
								"couldn't find valid <value> type");
						}

						return value;
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing a <value> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #15
0
파일: Primary.cs 프로젝트: GlenDC/L20n.cs
					public static AST.INode Parse(CharStream stream)
					{
						var startingPos = stream.Position;
						
						try
						{
							AST.INode primary;

							if (Literal.PeekAndParse(stream, out primary))
								return primary;

							if (Value.PeekAndParse(stream, out primary))
								return primary;
							
							return IdentifierExpression.Parse(stream);
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing a <primary> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #16
0
					public static AST.INode Parse(CharStream stream)
					{
						var startingPos = stream.Position;
						
						try
						{
							if (stream.SkipIfPossible('('))
							{
								var e = Expression.Parse(stream);
								stream.SkipCharacter(')');
								return e;
							} else
							{ // than we /should/ have a primary expressions
								return Primary.Parse(stream);
							}
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <parenthesis_expression> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #17
0
파일: Property.cs 프로젝트: GlenDC/L20n.cs
					public static AST.INode Parse(CharStream stream, AST.PropertyExpression property = null)
					{
						var startingPos = stream.Position;
						
						try
						{
							if (property == null)
							{
								var obj = IdentifierExpression.Parse(stream);
								property = new AST.PropertyExpression(obj);
							}

							char c;
							// can be either a simple identifier ('.') or expression ('[')
							while (stream.SkipAnyIfPossible(new char[] {'.', '['}, out c))
							{
								if (c == '.')
								{
									property.Add(Identifier.Parse(stream, false));
								} else
								{
									WhiteSpace.Parse(stream, true);
									property.Add(Expression.Parse(stream));
									WhiteSpace.Parse(stream, true);
									stream.SkipCharacter(']');
								}
							}
							
							return property;
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <property_expression> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #18
0
파일: HashValue.cs 프로젝트: GlenDC/L20n.cs
				public static AST.INode Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{
						// skip opening tag
						stream.SkipCharacter('{');

						// at least 1 hashItem is required with optional whitespace surrounding it
						var hashValue = new AST.HashValue();
						WhiteSpace.Parse(stream, true);
						hashValue.AddItem(HashItem.Parse(stream));
						WhiteSpace.Parse(stream, true);

						// parse all other (optional) hashItems
						while (stream.SkipIfPossible(','))
						{
							if (!HashValue.ParseHashItem(stream, hashValue))
							{
								// if we have a  trailing comma, it will be break here
								break;
							}
						}

						// skip closing tag
						stream.SkipCharacter('}');

						return hashValue;
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing a <hash_value> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}
예제 #19
0
파일: Binary.cs 프로젝트: GlenDC/L20n.cs
					public static AST.INode Parse(CharStream stream)
					{
						var startingPos = stream.Position;
						
						try
						{
							var first = Unary.Parse(stream);
							string raw;
							if (stream.ReadReg(@"\s*(\=\=|\!\=|\<\=|\>\=|\<|\>|\+|\-|\*|\/|\%)", out raw))
							{
								var chain = new Chain(stream, first, raw);
								return chain.Build();
							} else
							{
								return first;
							}
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <binary_expression> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #20
0
파일: Unary.cs 프로젝트: GlenDC/L20n.cs
					public static AST.INode Parse(CharStream stream)
					{
						var startingPos = stream.Position;
						
						try
						{
							char op;
							if (stream.SkipAnyIfPossible(new char[3]{'+', '-', '!'}, out op))
							{
								WhiteSpace.Parse(stream, true);
								var expression = Unary.Parse(stream);
								return new AST.UnaryOperation(expression, op);
							} else
							{
								return Member.Parse(stream);
							}
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <unary_expression> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #21
0
					public static AST.INode Parse(CharStream stream)
					{
						var startingPos = stream.Position;

						try
						{
							AST.INode identifier;

							if (Variable.PeekAndParse(stream, out identifier))
								return identifier;
							
							if (Global.PeekAndParse(stream, out identifier))
								return identifier;

							return new AST.Identifier(
								Identifier.Parse(stream, true));
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <identifier> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #22
0
파일: Entry.cs 프로젝트: GlenDC/L20n.cs
				public static void Parse(CharStream stream, Internal.LocaleContext.Builder builder)
				{
					var startingPos = stream.Position;
					
					try
					{
						if (Comment.PeekAndParse(stream))
							return;

						// normally we keep the requirements of a format for a parser internal,
						// but in this case we have the same start for both a <macro> and an <entity>
						// so we simply have to make an exception in this case for performance reasons
						if (stream.SkipIfPossible('<'))
						{
							var identifier = Identifier.Parse(stream, true);

							if (Macro.PeekAndParse(stream, identifier, builder))
								return;

							// now it NEEDS to be a entitiy, else our input is simply invalid
							// knowing that we are already on a path of no return
							// because of the fact that we started parsing '<' and an identifier.
							Entity.Parse(stream, identifier, builder);
						} else
						{
							// it has to be an import statement at this point
							ImportStatement.Parse(stream, builder);
						}
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing an <entry> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new ParseException(msg, e);
					}
				}
예제 #23
0
파일: Member.cs 프로젝트: GlenDC/L20n.cs
					public static AST.INode Parse(CharStream stream)
					{
						var startingPos = stream.Position;

						try
						{
							AST.INode expression;

							if (Attribute.PeekAndParse(stream, out expression))
							// an attribute expression is always seperated by '::' and optionally with square brackets
							// around the actual attribute identifier, that allows the use of an expression
							{
								return expression;
							} else if (Property.PeekAndParse(stream, out expression))
							// a property expression is always seperated by a dot (`.`) or embraced by square brackets
							// and has to have at least 1 property (e.g.: `x.y` or `x[y]`),
							// more than 1 is also acceptable (e.g.: `x.y.z` or `x[y][z]` or `x[y].z` or `x.y[z]`).
							{
								return expression;
							} else
							{
								var member = Parenthesis.Parse(stream);

								if (Call.PeekAndParse(stream, member, out expression))
									return expression;
	
								return member;
							}
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <member_expression> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #24
0
파일: Call.cs 프로젝트: GlenDC/L20n.cs
					public static AST.INode Parse(CharStream stream, AST.INode member)
					{
						var startingPos = stream.Position;

						try
						{
							// skip opening tag
							stream.SkipCharacter('(');
							WhiteSpace.Parse(stream, true);

							var call = new AST.CallExpression(member);

							// parse arguments if possible
							if (!stream.SkipIfPossible(')'))
							{
								call.AddParameter(ParseExpression(stream));

								// but we can also have moreß
								while (stream.SkipIfPossible(','))
								{
									call.AddParameter(ParseExpression(stream));
								}

								// skip closing tag
								stream.SkipCharacter(')');
							}

							return call;
						} catch (Exception e)
						{
							string msg = String.Format(
								"something went wrong parsing an <call_expression> starting at {0}",
								stream.ComputeDetailedPosition(startingPos));
							throw new Exceptions.ParseException(msg, e);
						}
					}
예제 #25
0
				public static AST.INode Parse(CharStream stream)
				{
					var startingPos = stream.Position;
					
					try
					{
						// Parse the quote and store that in the AST of the stringValue
						var quote = Quote.Parse(stream);
						var value = new AST.StringValue(quote);

						AST.INode expression;
						char c;

						// Trim starting space
						WhiteSpace.Parse(stream, true);
			
						// as long as we have more characters left
						// we'll keep reading, and break from within this loop body
						// when we reached the same quote that started this entire parser logic.
						while ((c = stream.PeekNext()) != '\0')
						{
							if (c == '\\')
							{
								// skip the escape character && read the next one
								stream.Skip();
								c = stream.PeekNext();
								if (c == '\\' || c == '{' || c == '}' || c == '\'' || c == '"')
								{
									c = stream.ForceReadNext();
									value.appendChar(c);
								} else if (c == 'u')
								{
									// skip the starter character
									stream.Skip();
									// try to read unicode character
									value.appendChar(ReadUnicodeCharacter(stream));
								} else
								{
									var msg = String.Format(
										"character \\{0} is not a valid escape character", c);
									throw stream.CreateException(msg);
								}
							// unicode characters are also supported in the classical U+xxxxxx notation
							} else if (c == 'U' && stream.PeekNext(1) == '+')
							{
								// skip the starter characters
								stream.Skip(2);
								// try to read unicode character
								value.appendChar(ReadUnicodeCharacter(stream));
							} else if (c == '\n' || c == '\r') // newlines get converted to a space
							{
								stream.Skip();
								var lc = value.LastCharacter;
								// we add this dummy character, such that we wouldn't add a space in
								// languages that don't use a space in general, such as chinese.
								if (!Char.IsWhiteSpace(lc) && lc != AST.StringValue.DummyNewlineWhitespaceCharacter)
									value.appendChar(AST.StringValue.DummyNewlineWhitespaceCharacter);
							} else
							{
								if (Quote.Peek(stream, quote))
								{
									break; // un-escaped quote means we're ending the string
								} else if (Expander.PeekAndParse(stream, out expression))
								{
									value.appendExpression(expression);
								} else
								{
									c = stream.ForceReadNext();
									var lc = value.LastCharacter;
									if(!Char.IsWhiteSpace(c)
									   	|| (lc != AST.StringValue.DummyNewlineWhitespaceCharacter
									        && !Char.IsWhiteSpace(lc)))
										value.appendChar(c);
								}
							}
						}

						// Eventually we expect exactly the same string back
						Quote.Parse(stream, quote);
						
						return value;
					} catch (Exception e)
					{
						string msg = String.Format(
							"something went wrong parsing an <string_value> starting at {0}",
							stream.ComputeDetailedPosition(startingPos));
						throw new Exceptions.ParseException(msg, e);
					}
				}