//
// Authors:
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
+// Marek Habersack <mhabersack@novell.com>
//
// (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
-// Copyright (c) 2004,2006 Novell, Inc (http://www.novell.com)
+// Copyright (c) 2004-2009 Novell, Inc (http://www.novell.com)
//
//
//
using System;
using System.Collections;
+using System.Collections.Generic;
using System.CodeDom.Compiler;
using System.Globalization;
using System.IO;
using System.Web.UI.HtmlControls;
using System.Web.Util;
-#if NET_2_0
-using System.Collections.Generic;
-#endif
-
namespace System.Web.Compilation
{
class BuilderLocation
public BuilderLocation (ControlBuilder builder, ILocation location)
{
this.Builder = builder;
- this.Location = location;
+ this.Location = new Location (location);
}
}
if (tags.Count == 0)
return false;
- return 0 == String.Compare (tagid, (string) tags.Peek (), true, CultureInfo.InvariantCulture);
+ return 0 == String.Compare (tagid, (string) tags.Peek (), true, Helpers.InvariantCulture);
}
public int Count {
}
}
+ enum TextBlockType
+ {
+ Verbatim,
+ Expression,
+ Tag,
+ Comment
+ }
+
+ sealed class TextBlock
+ {
+ public string Content;
+ public readonly TextBlockType Type;
+ public readonly int Length;
+
+ public TextBlock (TextBlockType type, string content)
+ {
+ Content = content;
+ Type = type;
+ Length = content.Length;
+ }
+
+ public override string ToString ()
+ {
+ return this.GetType ().FullName + " [" + this.Type + "]";
+ }
+ }
+
class AspGenerator
{
-#if NET_2_0
const int READ_BUFFER_SIZE = 8192;
internal static Regex DirectiveRegex = new Regex (@"<%\s*@(\s*(?<attrname>\w[\w:]*(?=\W))(\s*(?<equal>=)\s*""(?<attrval>[^""]*)""|\s*(?<equal>=)\s*'(?<attrval>[^']*)'|\s*(?<equal>=)\s*(?<attrval>[^\s%>]*)|(?<equal>)(?<attrval>\s*?)))*\s*?%>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
-#endif
+
+ static readonly Regex runatServer = new Regex (@"<[\w:\.]+.*?runat=[""']?server[""']?.*?/?>",
+ RegexOptions.Compiled | RegexOptions.Singleline |
+ RegexOptions.Multiline | RegexOptions.IgnoreCase |
+ RegexOptions.CultureInvariant);
+
+ static readonly Regex endOfTag = new Regex (@"</[\w:\.]+\s*?>",
+ RegexOptions.Compiled | RegexOptions.Singleline |
+ RegexOptions.Multiline | RegexOptions.IgnoreCase |
+ RegexOptions.CultureInvariant);
+
+ static readonly Regex expressionRegex = new Regex (@"<%.*?%>",
+ RegexOptions.Compiled | RegexOptions.Singleline |
+ RegexOptions.Multiline | RegexOptions.IgnoreCase |
+ RegexOptions.CultureInvariant);
+
+ static readonly Regex clientCommentRegex = new Regex (@"<!--(.|\s)*?-->",
+ RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase |
+ RegexOptions.CultureInvariant);
+
ParserStack pstack;
BuilderLocationStack stack;
TemplateParser tparser;
bool inForm;
bool useOtherTags;
TagType lastTag;
-#if NET_2_0
AspComponentFoundry componentFoundry;
Stream inputStream;
{
this.componentFoundry = componentFoundry;
}
-#endif
public AspGenerator (TemplateParser tparser)
{
this.tparser = tparser;
text = new StringBuilder ();
stack = new BuilderLocationStack ();
-
-#if !NET_2_0
- rootBuilder = new RootBuilder (tparser);
- tparser.RootBuilder = rootBuilder;
- stack.Push (rootBuilder, null);
-#endif
pstack = new ParserStack ();
}
get { return pstack.Filename; }
}
-#if NET_2_0
PageParserFilter PageParserFilter {
get {
if (tparser == null)
if (matches == null || matches.Count == 0)
return null;
- string wantedDirectiveName = tparser.DefaultDirectiveName.ToLower ();
+ string wantedDirectiveName = tparser.DefaultDirectiveName.ToLower (Helpers.InvariantCulture);
string directiveName;
GroupCollection groups;
CaptureCollection ccNames;
if (String.IsNullOrEmpty (directiveName))
continue;
- if (String.Compare (directiveName.ToLower (), wantedDirectiveName, StringComparison.Ordinal) != 0)
+ if (String.Compare (directiveName.ToLower (Helpers.InvariantCulture), wantedDirectiveName, StringComparison.Ordinal) != 0)
continue;
var loc = new Location (null);
stack.Push (rootBuilder, null);
tparser.RootBuilder = rootBuilder;
}
-#endif
BaseCompiler GetCompilerFromType ()
{
if (type == typeof (UserControlParser))
return new UserControlCompiler ((UserControlParser) tparser);
-#if NET_2_0
+
if (type == typeof(MasterPageParser))
return new MasterPageCompiler ((MasterPageParser) tparser);
-#endif
throw new Exception ("Got type: " + type);
}
parser.Error += new ParseErrorHandler (ParseError);
parser.TagParsed += new TagParsedHandler (TagParsed);
parser.TextParsed += new TextParsedHandler (TextParsed);
-#if NET_2_0
parser.ParsingComplete += new ParsingCompleteHandler (ParsingCompleted);
tparser.AspGenerator = this;
CreateRootBuilder (inputStream, filename);
-#endif
+
if (!pstack.Push (parser))
throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
}
}
-#if NET_2_0
void InitParser (string filename)
{
StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
InitParser (reader, filename);
}
-#endif
void CheckForDuplicateIds (ControlBuilder root, Stack scopes)
{
return;
if (scopes == null)
- scopes = new Stack ();
-
-#if NET_2_0
+ scopes = new Stack ();
+
Dictionary <string, bool> ids;
-#else
- Hashtable ids;
-#endif
if (scopes.Count == 0 || root.IsNamingContainer) {
-#if NET_2_0
ids = new Dictionary <string, bool> (StringComparer.Ordinal);
-#else
- ids = new Hashtable ();
-#endif
scopes.Push (ids);
} else {
-#if NET_2_0
ids = scopes.Peek () as Dictionary <string, bool>;
-#else
- ids = scopes.Peek () as Hashtable;
-#endif
}
if (ids == null)
public void Parse (string file)
{
-#if ONLY_1_1
- Parse (file, true);
-#else
Parse (file, false);
-#endif
}
public void Parse (TextReader reader, string filename, bool doInitParser)
if (text.Length > 0)
FlushText ();
-#if NET_2_0
tparser.MD5Checksum = pstack.Parser.MD5Checksum;
-#endif
pstack.Pop ();
#if DEBUG
public void Parse (Stream stream, string filename, bool doInitParser)
{
-#if NET_2_0
inputStream = stream;
-#endif
Parse (new StreamReader (stream, WebEncoding.FileEncoding), filename, doInitParser);
}
public void Parse ()
{
-#if NET_2_0
string inputFile = tparser.InputFile;
TextReader inputReader = tparser.Reader;
if (inputReader != null)
inputReader.Close ();
}
-#else
- Parse (Path.GetFullPath (tparser.InputFile));
-#endif
}
internal static void AddTypeToCache (ArrayList dependencies, string inputFile, Type type)
}
Parse ();
-
BaseCompiler compiler = GetCompilerFromType ();
type = compiler.GetCompiledType ();
// The kludge supports only self-closing tags inside attributes.
//
// KLUDGE WARNING!!
- static readonly Regex runatServer=new Regex (@"<[\w:\.]+.*?runat=[""']?server[""']?.*?/>",
- RegexOptions.Compiled | RegexOptions.Singleline |
- RegexOptions.Multiline | RegexOptions.IgnoreCase |
- RegexOptions.CultureInvariant);
bool ProcessTagsInAttributes (ILocation location, string tagid, TagAttributes attributes, TagType type)
{
if (attributes == null || attributes.Count == 0)
TextParsed (location, String.Format (" {0}=\"{1}", key, index > 0 ? value.Substring (0, index) : String.Empty));;
FlushText ();
- ParseAttributeTag (group.Value);
+ ParseAttributeTag (group.Value, location);
if (index + length < value.Length)
TextParsed (location, value.Substring (index + length) + "\"");
else
return retval;
}
- void ParseAttributeTag (string code)
+ void ParseAttributeTag (string code, ILocation location)
{
- AspParser parser = new AspParser ("@@attribute_tag@@", new StringReader (code));
+ AspParser outerParser = location as AspParser;
+ int positionOffset = outerParser != null ? outerParser.BeginPosition : 0;
+ AspParser parser = new AspParser ("@@attribute_tag@@", new StringReader (code), location.BeginLine - 1, positionOffset, outerParser);
parser.Error += new ParseErrorHandler (ParseError);
parser.TagParsed += new TagParsedHandler (TagParsed);
parser.TextParsed += new TextParsedHandler (TextParsed);
FlushText ();
}
-#if NET_2_0
void ParsingCompleted ()
{
PageParserFilter pfilter = PageParserFilter;
pfilter.ParseComplete (RootBuilder);
}
-#endif
void CheckIfIncludeFileIsSecure (string filePath)
{
if (exception != null || !StrUtils.StartsWith (newdir, HttpRuntime.AppDomainAppPath))
throw new ParseException (Location, "Files above the application's root directory cannot be included.");
}
+
+ string ChopOffTagStart (ILocation location, string content, string tagid)
+ {
+ string tagstart = '<' + tagid;
+ if (content.StartsWith (tagstart)) {
+ TextParsed (location, tagstart);
+ content = content.Substring (tagstart.Length);
+ }
+
+ return content;
+ }
void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
{
if (text.Length != 0)
FlushText (lastTag == TagType.CodeRender);
- if (0 == String.Compare (tagid, "script", true, CultureInfo.InvariantCulture)) {
+ if (0 == String.Compare (tagid, "script", true, Helpers.InvariantCulture)) {
bool in_script = (inScript || ignore_text);
if (in_script) {
if (ProcessScript (tagtype, attributes))
{
string plainText = location.PlainText;
if (!ProcessTagsInAttributes (location, tagid, attributes, TagType.Tag))
- TextParsed (location, plainText);
+ TextParsed (location, ChopOffTagStart (location, plainText, tagid));
}
break;
case TagType.Close:
if (!ProcessTag (location, tagid, attributes, tagtype, out tagIgnored) && !tagIgnored) {
string plainText = location.PlainText;
if (!ProcessTagsInAttributes (location, tagid, attributes, TagType.SelfClosing))
- TextParsed (location, plainText);
+ TextParsed (location, ChopOffTagStart (location, plainText, tagid));
} else if (stack.Count != count) {
CloseControl (tagid);
}
if (isvirtual) {
bool parsed = false;
-#if NET_2_0
VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
if (vpp.FileExists (file)) {
parsed = true;
}
}
-#endif
if (!parsed)
Parse (tparser.MapPath (file), true);
for (int idx = otags.Count - 1; idx >= 0; idx--) {
string otagid = (string) otags [idx];
- if (0 == String.Compare (tagid, otagid, true, CultureInfo.InvariantCulture)) {
+ if (0 == String.Compare (tagid, otagid, true, Helpers.InvariantCulture)) {
do {
otags.RemoveAt (idx);
} while (otags.Count - 1 >= idx);
return Path.GetFullPath (Path.Combine (basedir, filename));
}
+
+ delegate bool CheckBlockEnd (string text);
- void TextParsed (ILocation location, string text)
+ bool CheckTagEndNeeded (string text)
{
- if (ignore_text)
- return;
-
- // Another gross hack - get rid of comments in the parsed text
- int textLen = text.Length;
- int textMaxIndex = textLen - 1;
- int commentStart = text.IndexOf ("<!--");
- int commentLastStart = 0;
-#if NET_2_0
- List <int> commentRanges = null;
-#else
- ArrayList commentRanges = null;
-#endif
+ return !text.EndsWith ("/>");
+ }
+
+ List <TextBlock> FindRegexBlocks (Regex rxStart, Regex rxEnd, CheckBlockEnd checkEnd, IList blocks, TextBlockType typeForMatches, bool discardBlocks)
+ {
+ var ret = new List <TextBlock> ();
+ foreach (TextBlock block in blocks) {
+ if (block.Type != TextBlockType.Verbatim) {
+ ret.Add (block);
+ continue;
+ }
- while (commentStart != -1) {
- int commentEnd = text.IndexOf ("-->", commentStart);
+ int lastIndex = 0, index;
+ MatchCollection matches = rxStart.Matches (block.Content);
+ bool foundMatches = matches.Count > 0;
+ foreach (Match match in matches) {
+ foundMatches = true;
+ index = match.Index;
+ if (lastIndex < index)
+ ret.Add (new TextBlock (TextBlockType.Verbatim, block.Content.Substring (lastIndex, index - lastIndex)));
- if (commentEnd == -1) {
- if (commentStart == 0)
- return;
- commentEnd = textMaxIndex;
- }
+ string value = match.Value;
+ if (rxEnd != null && checkEnd (value)) {
+ int startFrom = index + value.Length;
+ Match m = rxEnd.Match (block.Content, startFrom);
+ if (m.Success)
+ value += block.Content.Substring (startFrom, m.Index - startFrom) + m.Value;
+ }
- if (commentRanges == null) {
-#if NET_2_0
- commentRanges = new List <int> ();
-#else
- commentRanges = new ArrayList ();
-#endif
+ if (!discardBlocks)
+ ret.Add (new TextBlock (typeForMatches, value));
+ lastIndex = index + value.Length;
}
- if (commentStart > commentLastStart) {
- commentRanges.Add (commentLastStart);
- commentRanges.Add (commentStart);
- }
+ if (lastIndex > 0 && lastIndex < block.Content.Length)
+ ret.Add (new TextBlock (TextBlockType.Verbatim, block.Content.Substring (lastIndex)));
- if (commentEnd == textMaxIndex)
- break;
-
- commentLastStart = commentEnd + 3;
- if (commentLastStart > textMaxIndex)
- break;
-
- commentStart = text.IndexOf ("<!--", commentLastStart);
- if (commentStart == -1) {
- int tailLength = textMaxIndex - commentLastStart;
- if (tailLength > 0) {
- commentRanges.Add (commentLastStart);
- commentRanges.Add (tailLength);
- }
- break;
- }
+ if (!foundMatches)
+ ret.Add (block);
}
- if (commentRanges != null) {
- if (commentRanges.Count == 0)
- return;
-
- StringBuilder sb = new StringBuilder ();
- for (int i = 0; i < commentRanges.Count; i += 2) {
-#if NET_2_0
- sb.Append (text.Substring (commentRanges [i], commentRanges [i + 1]));
-#else
- sb.Append (text.Substring ((int)commentRanges [i], (int)commentRanges [i + 1]));
-#endif
- }
+ return ret;
+ }
+
+ IList SplitTextIntoBlocks (string text)
+ {
+ var ret = new List <TextBlock> ();
- string noComments = sb.ToString ().Trim ();
- if (noComments.Length == 0)
- return;
+ ret.Add (new TextBlock (TextBlockType.Verbatim, text));
+ ret = FindRegexBlocks (clientCommentRegex, null, null, ret, TextBlockType.Comment, false);
+ ret = FindRegexBlocks (runatServer, endOfTag, CheckTagEndNeeded, ret, TextBlockType.Tag, false);
+ ret = FindRegexBlocks (expressionRegex, null, null, ret, TextBlockType.Expression, false);
- text = noComments;
- }
+ return ret;
+ }
- // And again... the first one wins - if we have expressions and server-side
- // controls together in one block of plain text, tough luck...
- if (text.IndexOf ("<%") != -1 && !inScript) {
- if (this.text.Length > 0)
- FlushText (true);
- CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
- r.AddChildren (this);
+ void TextParsed (ILocation location, string text)
+ {
+ if (ignore_text)
+ return;
+
+ if (inScript) {
+ this.text.Append (text);
+ FlushText (true);
return;
}
- int startIndex = 0, index = 0;
- Match match;
+ IList blocks = SplitTextIntoBlocks (text);
+ foreach (TextBlock block in blocks) {
+ switch (block.Type) {
+ case TextBlockType.Verbatim:
+ this.text.Append (block.Content);
+ break;
- while (index > -1 && startIndex < textLen) {
- match = runatServer.Match (text, index);
-
- if (match.Success) {
- string value = match.Value;
- index = match.Index;
- if (index > startIndex)
- this.text.Append (text.Substring (startIndex, index - startIndex));
- ParseAttributeTag (value);
- index += value.Length;
- startIndex = index;
- } else
- break;
+ case TextBlockType.Expression:
+ if (this.text.Length > 0)
+ FlushText (true);
+ CodeRenderParser r = new CodeRenderParser (block.Content, stack.Builder, location);
+ r.AddChildren (this);
+ break;
- if (index < textLen)
- index = text.IndexOf ('<', index);
- else
- break;
+ case TextBlockType.Tag:
+ ParseAttributeTag (block.Content, location);
+ break;
+
+ case TextBlockType.Comment: {
+ this.text.Append ("<!--");
+ FlushText (true);
+ string blockToParse = block.Content.Substring (4, block.Length - 7);
+ bool condEndif;
+ if (blockToParse.EndsWith ("<![endif]")) {
+ blockToParse = blockToParse.Substring (0, blockToParse.Length - 9);
+ condEndif = true;
+ } else
+ condEndif = false;
+
+ AspParser outerParser = location as AspParser;
+ int positionOffset = outerParser != null ? outerParser.BeginPosition : 0;
+ AspParser parser = new AspParser ("@@comment_code@@", new StringReader (blockToParse), location.BeginLine - 1, positionOffset, outerParser);
+ parser.Error += new ParseErrorHandler (ParseError);
+ parser.TagParsed += new TagParsedHandler (TagParsed);
+ parser.TextParsed += new TextParsedHandler (TextParsed);
+ parser.Parse ();
+ if (condEndif)
+ this.text.Append ("<![endif]");
+ this.text.Append ("-->");
+ FlushText (true);
+ break;
+ }
+ }
}
-
- this.text.Append (text.Substring (startIndex));
- //PrintLocation (location);
}
void FlushText ()
return;
if (inScript) {
-#if NET_2_0
PageParserFilter pfilter = PageParserFilter;
if (pfilter != null && !pfilter.ProcessCodeConstruct (CodeConstructType.ScriptTag, t))
return;
-#endif
+
tparser.Scripts.Add (new ServerSideScript (t, new System.Web.Compilation.Location (tparser.Location)));
return;
}
}
}
-#if NET_2_0
bool BuilderHasOtherThan (Type type, ControlBuilder cb)
{
ArrayList al = cb.OtherTags;
return true;
}
-#endif
public void AddControl (Type type, IDictionary attributes)
{
{
ignored = false;
if (isApplication) {
- if (String.Compare (tagid, "object", true, CultureInfo.InvariantCulture) != 0)
+ if (String.Compare (tagid, "object", true, Helpers.InvariantCulture) != 0)
throw new ParseException (location, "Invalid tag for application file.");
}
ControlBuilder parent = stack.Builder;
ControlBuilder builder = null;
if (parent != null && parent.ControlType == typeof (HtmlTable) &&
- (String.Compare (tagid, "thead", true, CultureInfo.InvariantCulture) == 0 ||
- String.Compare (tagid, "tbody", true, CultureInfo.InvariantCulture) == 0)) {
+ (String.Compare (tagid, "thead", true, Helpers.InvariantCulture) == 0 ||
+ String.Compare (tagid, "tbody", true, Helpers.InvariantCulture) == 0)) {
ignored = true;
return true;
}
string plainText = location.PlainText;
if (!runatServer && plainText.IndexOf ("<%$") == -1&& plainText.IndexOf ("<%") > -1)
return false;
-#if NET_2_0
+
PageParserFilter pfilter = PageParserFilter;
if (pfilter != null && !pfilter.AllowControl (builder.ControlType, builder))
throw new ParseException (Location, "Control type '" + builder.ControlType + "' not allowed.");
if (!OtherControlsAllowed (builder))
throw new ParseException (Location, "Only Content controls are allowed directly in a content page that contains Content controls.");
-#endif
builder.Location = location;
builder.ID = htable ["id"] as string;
CheckLanguage (language);
string src = (string) attributes ["src"];
if (src != null) {
- if (src == "")
+ if (src.Length == 0)
throw new ParseException (Parser,
"src cannot be an empty string");
Parser.VerbatimID = "script";
javascript = true;
}
- TextParsed (location, location.PlainText);
+ string content = location.PlainText;
+ /* HACK, HACK, HACK */
+ if (content.StartsWith ("<script")) {
+ TextParsed (location, "<script");
+ content = content.Substring (7);
+ }
+
+ TextParsed (location, content);
return true;
}
}
{
ControlBuilder current = stack.Builder;
string btag = current.OriginalTagName;
- if (String.Compare (btag, "tbody", true, CultureInfo.InvariantCulture) != 0 &&
- String.Compare (tagid, "tbody", true, CultureInfo.InvariantCulture) == 0) {
+ if (String.Compare (btag, "tbody", true, Helpers.InvariantCulture) != 0 &&
+ String.Compare (tagid, "tbody", true, Helpers.InvariantCulture) == 0) {
if (!current.ChildrenAsProperties) {
try {
TextParsed (location, location.PlainText);
return true;
}
- if (current.ControlType == typeof (HtmlTable) && String.Compare (tagid, "thead", true, CultureInfo.InvariantCulture) == 0)
+ if (current.ControlType == typeof (HtmlTable) && String.Compare (tagid, "thead", true, Helpers.InvariantCulture) == 0)
return true;
- if (0 != String.Compare (tagid, btag, true, CultureInfo.InvariantCulture))
+ if (0 != String.Compare (tagid, btag, true, Helpers.InvariantCulture))
return false;
// if (current is TemplateBuilder)
return true;
}
-#if NET_2_0
CodeConstructType MapTagTypeToConstructType (TagType tagtype)
{
switch (tagtype) {
}
}
-#endif
bool ProcessCode (TagType tagtype, string code, ILocation location)
{
-#if NET_2_0
PageParserFilter pfilter = PageParserFilter;
// LAMESPEC:
//
//
if (pfilter != null && (!pfilter.AllowCode || pfilter.ProcessCodeConstruct (MapTagTypeToConstructType (tagtype), code)))
return true;
-#endif
+
ControlBuilder b = null;
if (tagtype == TagType.CodeRender)
b = new CodeRenderBuilder (code, false, location);
if (lang == null || lang == "")
return;
- if (String.Compare (lang, tparser.Language, true, CultureInfo.InvariantCulture) == 0)
+ if (String.Compare (lang, tparser.Language, true, Helpers.InvariantCulture) == 0)
return;
-#if NET_2_0
CompilationSection section = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
if (section.Compilers[tparser.Language] != section.Compilers[lang])
-#else
- CompilationConfiguration cfg = CompilationConfiguration.GetInstance (HttpContext.Current);
- if (!cfg.Compilers.CompareLanguages (tparser.Language, lang))
-#endif
throw new ParseException (Location,
String.Format ("Trying to mix language '{0}' and '{1}'.",
tparser.Language, lang));
string str;
ControlBuilder builder;
AspGenerator generator;
+ ILocation location;
- public CodeRenderParser (string str, ControlBuilder builder)
+ public CodeRenderParser (string str, ControlBuilder builder, ILocation location)
{
this.str = str;
this.builder = builder;
+ this.location = location;
}
public void AddChildren (AspGenerator generator)
void DoParseExpressions (string str)
{
int startIndex = 0, index = 0;
- Regex codeDirective = new Regex ("(<%(?!@)(?<code>.*?)%>)|(<[\\w:\\.]+.*?runat=[\"']?server[\"']?.*?/>)",
+ Regex codeDirective = new Regex ("(<%(?!@)(?<code>(.|\\s)*?)%>)|(<[\\w:\\.]+.*?runat=[\"']?server[\"']?.*?/>)",
RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
Match match;
int strLen = str.Length;
void DoParse (string str)
{
- AspParser parser = new AspParser ("@@nested_tag@@", new StringReader (str));
+ AspParser outerParser = location as AspParser;
+ int positionOffset = outerParser != null ? outerParser.BeginPosition : 0;
+ AspParser parser = new AspParser ("@@code_render@@", new StringReader (str), location.BeginLine - 1, positionOffset, outerParser);
parser.Error += new ParseErrorHandler (ParseError);
parser.TagParsed += new TagParsedHandler (TagParsed);
parser.TextParsed += new TextParsedHandler (TextParsed);