CSS 사전 프로세서(예: SASS)를 처음부터 구축하는 방법

만약 네가 인터넷 개발에 종사한다면, 아마도 너는 Sass, Less, 파그, Stylus 등등을 들어 본 적이 있을 것이다. 이 모든 것은 예비 프로세서이다.이 강좌에서, 우리는 처음부터 변수와 함수를 사용하여 기능성 css 프로세서를 구축할 것이다.이런 유형의 새로운 언어를 원본 컴파일이라고 부른다.만약 네가 매우 흥분한다면, 나는 너를 실망시키지 않기를 바란다.

로드맵 그리기


우선 우리가 무엇을 해야 하는지 봅시다.확장자를 사용하여 이 언어를 DotDot이라고 합니다.점을 찍다
새로운 언어를 설계할 때, 때로는 디자인에 쓰는 것이 매우 좋다.만약 당신이 진정으로 흐르는 것이 있다면, 당신은 인코딩을 시작할 수 있습니다.
다음은 점 세션입니다.
x = 1
y = 5
x-y-z = 6
col-z = berry

@f{
    border: 10px solid black;
    border-radius: 50%;
}

.add{
    color:white;
    background-color: rgb(3, 4, 5);
}

#ad{
    color: rgb(x, 4, 5);
}

.a div a{
    color: ..col-z;
    @f
}
다음과 같이 컴파일됩니다.
.add{
    color: white;
    background-color: rgb(3,4,5);
}

#ad{
    color: rgb(1,4,5);
}

.a div a{
    color: berry;
    border: 10px solid black;
    border-radius: 50%;
}

해부 설계


저희가 어떤 기능을 포함했는지 보여드릴게요.

변수


x = 1
y = 5
x-y-z = 6
col-z = berry
저희가 봤어요.
  • Js에서 $나 var 같은 설명자를 지정할 필요가 없습니다.
  • 추가할 수 있습니다. - 변수 이름
  • 우리도 함수 값에서 변수를 호출할 수 있다
    rgb(x, 4, 5);
    
    
    및 속성 값
    color: ..col-z;
    
    어디속성의 변수 호출을 직접 표시합니다.

    기능


    @f{
        border: 10px solid black;
        border-radius: 50%;
    }
    
    함수는 @ 기호로 표시됩니다.이러한 속성은 다음과 같은 경우에 포함되는 속성으로 확장됩니다.
    .a div a{
        color: ..col-z;
        @f
    }
    
    이것은 이미 충분히 복잡하다.우리 시작합시다!
    import copy # we have just one import
    
    우리는 원본을 변수로 삼는다
    source = '''
    
    x = 1
    y = 5
    x-y-z = 6
    col-z = berry
    
    @f{
        border: 10px solid black;
        border-radius: 50%;
    }
    
    .add{
        color:white;
        background-color: rgb(3, 4, 5);
    }
    
    #ad{
        color: rgb(x, 4, 5);
    }
    
    .a div a{
        color: ..col-z;
        @f
    }
    '''
    

    상수 정의


    우리는 상량을 한 종류에 묶을 것이다.
    class SYM:
        LEFT_BRACE = '{'
        RIGHT_BRACE = '}'
        LEFT_ROUND = '('
        RIGHT_ROUND = ')'
        NEW_LINE = '\n'
        TAB = '    '
        COLON = ':'
        SEMI_COLON = ';'
        SPACE = ' '
        COMMA = ','
        EQUAL = '='
        UNION = '-'
        DOT = '.'
        AT = '@'
    
    그리고 키워드를 정의하겠습니다.
    KEYWORDS = (SYM.LEFT_BRACE, SYM.RIGHT_BRACE, SYM.NEW_LINE, SYM.TAB, SYM.COLON, 
        SYM.SEMI_COLON, SYM.SPACE, SYM.RIGHT_ROUND, SYM.LEFT_ROUND, SYM.COMMA, SYM.EQUAL)
    

    Lexer 구축


    lexer의 코드는 this lexer tutorial 에서 왔습니다.필요하다면 한번 보세요.여기서 우리는 get\u lexeme 방법을 사용하여 코드를 하나의 클래스로 변환합니다.이 방법은 우리에게 목록을 제공했다.위의dotdot 세션은 아래lexemes 변수의 목록으로 변환됩니다.
    이것은 우리의 lexer 과정입니다.
    class Lexer:
        def __init__(self, source: str, KEYWORDS: list):
            self.white_space = SYM.SPACE
            self.KEYWORDS = KEYWORDS
            self.lexeme = ''
            self.lexemes = []
            self.string = source
    
        def get_lexemes(self) -> list:
            for i, char in enumerate(self.string):
                if char != self.white_space and char != SYM.NEW_LINE:
                    self.lexeme += char  # adding a char each time
                if (i+1 < len(self.string)):  # prevents error
                    if self.string[i+1] == self.white_space or self.string[i+1] in KEYWORDS or self.lexeme in KEYWORDS or self.string[i+1] == SYM.NEW_LINE: # if next char == ' '
                        if self.lexeme != '':
                            self.lexemes.append(self.lexeme)
                            self.lexeme = ''
            return self.lexemes
    
    그리고 기본 변수를 설명합니다.
    v = Lexer(source, KEYWORDS)
    lexemes = v.get_lexemes()
    
    lexemes.append('') # appending an unused last element to properly dump all values
    
    
    어소는 지금
    ['x', '=', '1', 'y', '=', '5', 'x-y-z', '=', '6', 'col-z', 
    '=', 'berry', '@f', '{', 'border', ':', '10px', 'solid', 
    'black', ';', 'border-radius', ':', '50%', ';', '}', '.add', 
    '{', 'color', ':', 'white', ';', 'background-color', ':', 
    'rgb', '(', '3', ',', '4', ',', '5', ')', ';', '}', '#ad', 
    '{', 'color', ':', 'rgb','(', 'x', ',', '4', ',', '5', ')', 
    ';', '}', '.a', 'div', 'a', '{', 'color', ':', '..col-z', 
    ';', '@f', '}', '']
    
    이런 분리 데이터가 있으면 처리하기가 훨씬 쉽다!

    기억의 개념


    다음은 모든 변수를 저장하기 위한 사전을 정의합니다.
    memory = {}
    
    어디
    x = 1
    
    앞으로 이렇게 될 거예요.
    {'x':'1'}
    
    그것을 되찾기 위해서 우리는 단지 할 수 있을 뿐이다
    memory['x']
    

    나무의 개념


    저희가 트리라는 사전이 있어요.
    tree = {}
    
    변환된 코드를
    {
        '@f': {
            'border': '10px solid black', 
            'border-radius': '50%'
        }, 
        '.add': {
            'color':'white', 
            'background-color': 'rgb ( 3 , 4 , 5 )'
        }, '#ad': {
            'color': 'rgb ( x ,4 , 5 )'
        }, 
        '.a div a': {
            'color': '..col-z', 
            '@f': ''
        }
    }
    
    우리의 다음 단계는 바로 어소 목록을 이 사전으로 바꾸는 것이다

    트리 생성


    우리가 있는 위치를 추적하기 위해서, 우리는 일련의 진실/거짓 값을 추출하는 변수가 있을 것이다 (켜기/끄기)

    소유자 설정


    id_string = ''
    last_id_string = ''
    last_attribute = ''
    
    current_attribute = ''
    current_value = ''
    
    len_lexemes = len(lexemes)
    
    id 문자열은 #add와 같습니다.xa div 등
    마지막 변수는 하위 섹션을 비우지 않은 변수만 포함합니다

    플래그 설정


    우리는 세 면의 깃발을 가지고 있을 것이다.
    우리가 블록을 시작할 때, {를 만났을 때, 그것은 진짜가 되고,}를 만났을 때, 그것은 가짜가 된다
    속성은 색상의 색상입니다: 검은색;
    진행 중인 속성은 전달할 때 true로 바뀌고, 전달할 때 false로 바뀝니다.
    value_진행 중인 것은 검사할 때true로 바뀝니다. 검사할 때false로 바뀝니다.
    block_ongoing = False
    attribute_ongoing = False
    value_ongoing = False
    
    우리는 목록에서 순환을 시작하고 위에서 설명한 표지를 실현할 것이다
    for i, lex in enumerate(lexemes):
    
        if i+1 < len_lexemes:
            next_lexeme = lexemes[i+1]
        prev_lexeme = lexemes[i-1]
    
        if lex == SYM.LEFT_BRACE:
            block_ongoing = True
            attribute_ongoing = True
            continue
        elif lex == SYM.RIGHT_BRACE:
            block_ongoing = False
            statement_ongoing = False
            attribute_ongoing = False
            value_ongoing = False
            continue
        if lex == SYM.COLON:
            value_ongoing = True
            attribute_ongoing = False
            continue
        elif lex == SYM.SEMI_COLON:
            value_ongoing = False
            statement_ongoing = False
            attribute_ongoing = True
            continue
    
    일단 우리가 표지판을 활성화하면, 우리는 할 일이 없고, 우리는 계속 전진해야 하기 때문에 계속해야 한다.

    처리 변수


    변수를 분배하기 위해서 우리는 = 기호를 기다린 후에 계속할 수 있다.
    그리고 변수 이름이나 값을 검사할 때, 우리는 순환이 계속되는 것을 막기 위해continue를 사용합니다
        if lex == SYM.EQUAL:
            memory[prev_lexeme] = next_lexeme
            continue
        elif next_lexeme == SYM.EQUAL or prev_lexeme == SYM.EQUAL:
            continue
    

    트리 구축 위치


    이제 저희가 로고를 사용할 거예요.
        if not block_ongoing:
            id_string += lex + ' '
        elif block_ongoing:
            if id_string:
                tree[id_string.strip()] = {}
                last_id_string = id_string.strip()
                id_string = ''
    
    여기서 우리가 처리하는 것은 블록 id입니다. 예를 들어 #add in #add {}입니다.저희가 해냈어요.
    tree[id_string.strip()] = {}
    
    여기 예시 나무가 하나 있어요.
    {
    '.add': {}
    }
    
    동일한 원칙을 사용하면 우리는 속성에 대해 이 조작을 실행할 것이다
        if attribute_ongoing:
            current_attribute += lex
        elif not attribute_ongoing:
            if current_attribute:
                tree[last_id_string][current_attribute] = ''
                last_attribute = current_attribute
                current_attribute = ''
    
    
    여기 예시 나무가 하나 있어요.
    {
    '.add': {
            'color':''
        }
    }
    
    
    가치
        if value_ongoing:
            current_value += lex + ' '
        elif not value_ongoing:
            if current_value:
                tree[last_id_string][last_attribute] = current_value.strip()
                last_value = current_value.strip()
                current_value = ''
    
    여기 예시 나무가 하나 있어요.
    {
    '.add': {
            'color':'white'
        }
    }
    
    
    이 부분은 우리의 트리 구축 블록을 끝냈다

    값 바꾸기


    이제 rgb()와 rgba() 등 함수의 변수 이름을 어떻게 바꾸는지 봅시다
    def parseFunc(f):
        v = f.split(SYM.LEFT_ROUND)
        name = v[0]
        vals = v[1][:-1].replace(SYM.SPACE, '')
        values = vals.split(SYM.COMMA)
        for i, v in enumerate(values):
            if not v.isnumeric():
                values[i] = memory[v]
        return '{}({})'.format(name.strip(), ','.join(values))
    
    여기서 우리는 메모리 사전의 x를 1로 바꾸고 rgb(x, 4, 5)를 rgb(1, 4, 5)로 바꿉니다.CSS 사전 프로세서를 처음부터 구축하기 위해서는 모든 상황을 고려해야 합니다.

    트리를 최종 형식으로 변환


    이제 우리는 위에서 정의한 함수를 포함하여 다시 트리를 훑어보고 함수를 전개해야 한다.
    기술적으로 말하자면, 우리는 사전을 교체할 때 그것을 변경할 수 없다.우리는 인용을 깨뜨리기 위해 그것을 복제해야 한다.우리는 그것을 깊이 복제해야 한다.
    ntree = copy.deepcopy(tree)
    
    그리고 우리는 교체한다.
    for block_name in ntree:
        properties = ntree[block_name]
        if block_name[0] == SYM.AT:
            continue
    
    현재, 만약 우리가 id의 첫 번째 기호를 @ 로 얻게 된다면, 우리는 그것을 건너뛰기만 하면 된다. 왜냐하면 우리는 확장된 정의를 필요로 하지 않기 때문이다.
        for element in properties:
            value = properties[element]
            if SYM.LEFT_ROUND in value:
                tree[block_name][element] = parseFunc(value)
    
    다음에 우리는 값 중 rgb () 와 유사한 함수가 있다면.이 경우 함수 parseFunc를 사용합니다.
            if SYM.DOT in value:
                tree[block_name][element] = memory[value.strip(SYM.DOT)]
    
    
    다음에 하나 볼게요.값에서, 우리는 메모리 사전에서 그것을 보고 교체합니다
            if SYM.AT in element:
                del tree[block_name][element]
                tree[block_name].update(tree[element])
    
    
    다음은 @ 기호를 블록의 키로 간주합니다. 이 블록의 사전에 사전을 추가하기만 하면 됩니다.
    이것 또한 데이터 구조를 사용하는 것이 일반 문자열보다 우수하다는 장점을 나타낸다.

    일반 Css로 컴파일


    이제 우리는 그것을 일반적인 CSS로 컴파일할 수 있습니다. 우리는 지금 그것만 인쇄합니다. (당신은 인쇄에서 file=btw를 사용할 수 있습니다.)
    for key in tree:
        if key[0] == SYM.AT:
            continue
        print(key, '{', sep='')
        for elem in tree[key]:
            print('{}{}: {};'.format(SYM.TAB, elem, tree[key][elem]))
        print('}\n')
    
    
    생기다
    .add{
        color: white;
        background-color: rgb(3,4,5);
    }
    
    #ad{
        color: rgb(1,4,5);
    }
    
    .a div a{
        color: berry;
        border: 10px solid black;
        border-radius: 50%;
    }
    
    

    It의 미래


    이것은 안전망이 없고 쿨한 기능이 부족한 상황에서 이루어진 것이지만, 이것은 정상적인 프로그래밍 논리를 통해 쿨한 것을 설정할 수 있다는 것을 증명하기 위한 것이다.이것은 내가 평소에 링크드 인 파이썬에 있는 프로젝트다.
    들여쓰기(예: 펜)를 기반으로 처음부터 CSS 프로세서를 구축하려면 this tutorial 를 사용하십시오.
    다음은 무엇을 추가해야 합니까?댓글은 아래와 같습니다!
    Github 링크: https://github.com/Abdur-rahmaanJ/dotdot
    미러 켜짐 PythonMembersClub
    unsplash에서 온 그림
    - 아부두르 라흐만 자한겔

    좋은 웹페이지 즐겨찾기