본문 바로가기

C#/근웹 연대기

c#으로 근본 없는 웹서버 개발기 24 : cshtml 파싱 - 파스노드 공통함수

⚠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);
    }
}

다음 포스팅에서는 각 노드에 대한 코드를 올릴 예정.