⚠WARNING
ASP.NET에 대한 포스팅이 아닙니다
나가실 문은 오른쪽 하단입니다
나가실 문은 오른쪽 하단입니다
이전 글에서 설명한,
각 노드에서 코드 사이즈를 줄이기 위하여 중첩되는 부분을 최상위 클래스인 ParseNode에 몰아주었다.
Razor구문과 C#코드의 분리가 명확하게 된다면 이상적이지만, 서로 간의 의존성이 높아서,
일목요연하고 싶은 마음이 상당히 트레이드오프 돼버렸다.
.. 아쉬움이 남지만, 아직은 내공이 많이 부족하다.
CsSection : C#의 {}, [], () 처럼 브라켓 구간의 시작과 끝 사이의 영역을 관장한다.
BlockSection, IndexSection, ArgsSection : 각각 {}, [], ()을 인수로 CsSection을 호출.
BlockStatement : if 또는 for 처럼 예약어와 함께 () 또는 {} 을 가지는 구문을 관리.
CsComment : 주석 진입 구간을 담당 (// or /* */)
CsString : 문자열상수 진입 및 탈출 구간을 담당.
NextCommentSkip : 주석 구간을 스킵하고 다음 토큰을 반환
Razor : Razor 구문 집입 구간을 담당.
RazorComment : Razor 코멘트 진입 및 탈출 구간을 담당 ( @* *@ )
<코드>
namespace dotweb;
public abstract partial class ParseNode
{
protected bool RazorComment()
{// @*로 시작하는 전역 코맨트
if (tk == "@" && Offset(1)?.First() == '*')
{
AddStr();
while (Next(true) != null) if (Offset(-1)?.Last() == '*' && tk == "@") break;
pi = i + 1; // 코맨트는 버린다.
Set_tk_toBack();
return true;
}
return false;
}
protected bool Razor()
{ // Razor 구문 전반.
if (tk != "@") return false;
int rcnt = 0;
for (int x = i; --x >= pi && tokens[x] == "@"; rcnt++) ;
if (rcnt % 2 == 1) return false;
if (RazorComment()) return true;
if (Offset(1) is "(" or "{")
{
AddStr();
Invoke(Offset(1) == "{" ? typeof(CS_inNode) : typeof(InlineNode));
return true;
}
if (GetType(Offset(-1)) == TTYPE.LITERAL) return false;
if (GetType(Offset(+1)) != TTYPE.LITERAL) return false;
AddStr();
Next();
pi = i; //@ 제거.
if (tk == "using")
{
Invoke(typeof(CS_UsingNode));
return true;
}
if (tk == "layout")
{
Next();
pi = i + 1;
Invoke(typeof(LayoutNode));
return true;
}
if (tk == "functions")
{
Next();
pi = i + 1;
Invoke(typeof(CS_FunctionNode));
return true;
}
if (tk == "section")
{
Next();
pi = i;
Invoke(typeof(SectionNode));
return true; ;
}
if ("if,do,for,foreach,while,look,switch,try".Split(",").Contains(tk))
{
BlockStatement();
return true;
}
Invoke(typeof(ImplicitNode));
return true;
}
private void BlockStatement(bool inSide = false)
{
var newChild = Invoke(typeof(CS_BlockNode));
if (inSide == true) //입양 절차
{
childs.Remove(newChild); //파양
var adoptive_parent = childs.Last() as CS_BlockNode;
newChild.parent = adoptive_parent; //부모가 되고
adoptive_parent.childs.Add(newChild);//이윽고 자식을 갖는다.
}
if (tk == "if")
{ // if.. else.. if.. else.. if.. else.. if..
var back = i;
NextCommentSkip();
if (tk == "else")
{ // I lost something else.
back = i;
NextCommentSkip();
if (tk != "if")
{
tk = "else";
i = back;
}
BlockStatement(true);
}
else i = back;
return;
}
if (tk is "try" or "catch")
{ // try + [catch] * N + finally.
var back = i;
NextCommentSkip();
if (tk is "catch" or "finally")
{
BlockStatement(true);
}
else i = back;
}
}
protected string NextCommentSkip()
{
Next(true);
while (CsComment() || RazorComment()) Next(true);
return tk;
}
protected bool CsComment()
{ // csharp 코맨트
if (tk != "/") return false;
if (Offset(1)?.First() == '*')
{
AddStr();
Next(true);
while (Next(true) != null)
{
if (tk == "/" && Offset(-1)?.Last() == '*') break;
}
pi = i + 1; //코맨트는 버린다.
Set_tk_toBack();
return true;
}
if (Offset(1)?.First() == '/')
{
AddStr();
NextRaw(true);
while (NextRaw(true) != null)
{
if (tk.Contains('\n')) break;
}
pi = i; //코맨트는 버린다.
Set_tk_toBack();
return true;
}
return false;
}
protected bool CsString()
{
if (tk == "\"" && Offset(-1) != "'")
{
AddStr(pi, i + 1);
Invoke(typeof(CS_StrNode));
return true;
}
return false;
}
private void CsSection(string st, string ed, bool cutOut, bool add)
{
do
{
if (tk == st) break;
CsComment();
RazorComment(); //VS2022에서는 구문오류가 된다.(???)
} while (Next() != null);
if (cutOut) pi = i + 1;
int closeCnt = 1;
while (Next() != null)
{
if (CsComment() || CsString() || Razor()) continue;
//태그 구간 찾기
if (st == "{" && tk == "<" && (GetType(Offset(1)) == TTYPE.LITERAL || Offset(1) == "@"))
{
string p = null;
for (int x = i; --x >= pi;)
{
if (!String.IsNullOrWhiteSpace(tokens[x]))
{
p = tokens[x];
break;
}
}
if (p == null)
{
for (int x = childs.Count; --x >= 0;)
{
if (childs[x] is string)
{
p = childs[x].ToString().Trim();
if (!String.IsNullOrWhiteSpace(p)) break;
}
else break;
}
if (p == null) p = ";";
}
if ("{};".Contains(p?.Last() ?? '?'))
{
AddStr();
Invoke(typeof(Tag_Node));
continue;
}
}
if (tk == st) closeCnt++;
else if (tk == ed && --closeCnt == 0) break;
}
if (add) AddStr();
if (cutOut) pi++;
}
protected void ArgsSection(bool cutOuter = false, bool add = true)
{
CsSection("(", ")", cutOuter, add);
}
protected void BlockSection(bool cutOuter = false, bool add = true)
{
CsSection("{", "}", cutOuter, add);
}
protected void IndexSection(bool cutOuter = false, bool add = true)
{
CsSection("[", "]", cutOuter, add);
}
}
다음 포스팅에서는 각 노드에 대한 코드를 올릴 예정.
'C# > 근웹 연대기' 카테고리의 다른 글
c#으로 근본 없는 웹서버 개발기 26 : cshtml 파싱 - 코드 조립 (0) | 2022.01.27 |
---|---|
c#으로 근본 없는 웹서버 개발기 25 : cshtml 파싱 - 파스노드(코드) (0) | 2022.01.24 |
c#으로 근본 없는 웹서버 개발기 23 : cshtml 파싱 - 파스노드 분류 (0) | 2022.01.19 |
c#으로 근본 없는 웹서버 개발기 22 : cshtml 파싱 - 공통클래스 (0) | 2022.01.16 |
c#으로 근본 없는 웹서버 개발기 21 : cshtml 파싱 - tokenize (0) | 2022.01.13 |