본문 바로가기

World Wide Web/위키 엔진 개발기

[위키 엔진 개발기] 1. 렌더러 개발 - 시작과 redirect

 사실 다른 기능부터 먼저 만들긴 했는데, 나무마크 렌더러에 대해서 먼저 써 보려고 한다. 사실 이걸 내 손으로 다시 만들게 된 이유는 시중에 돌아다니는 렌더러 코드 중 제대로 작동하는 게 없어서 그렇다. 그래서 앞으로 하게 된 삽질들이 여기 기록될 예정이다. 목표는 나무위키;문법 도움말을 넣고 돌렸을 때 문제없이 출력되는 것. 참고로 PHP다.

 

 시작

일단 여느 렌더러처럼 클래스를 활용하기로 했다. 대략 NamuMark라는 상위 클래스 속에 필요한 함수들이 들어 있는 방식이다.

public function __construct($txt){
        $this->wikitext = $txt;
        $this->heading_tags = [
            '/^====== (.*) ======$/' => ['level' => 6, 'folding' => false],
            '/^===== (.*) =====$/' => ['level' => 5, 'folding' => false],
            '/^==== (.*) ====$/' => ['level' => 4, 'folding' => false],
            '/^=== (.*) ===$/' => ['level' => 3, 'folding' => false],
            '/^== (.*) ==$/' => ['level' => 2, 'folding' => false],
            '/^= (.*) =$/' => ['level' => 1, 'folding' => false],
            '/^======# (.*) #======$/' => ['level' => 6, 'folding' => true],
            '/^=====# (.*) #=====$/' => ['level' => 5, 'folding' => true],
            '/^====# (.*) #====$/' => ['level' => 4, 'folding' => true],
            '/^===# (.*) #===$/' => ['level' => 3, 'folding' => true],
            '/^==# (.*) #==$/' => ['level' => 2, 'folding' => true],
            '/^=# (.*) #=$/' => ['level' => 1, 'folding' => true]
        ];
        $this->list_tags = [
            '*' => ['ordered' => false],
            '1.' => ['ordered' => true, 'type' => 'decimal'],
            'A.' => ['ordered' => true, 'type' => 'upper-alpha'],
            'a.' => ['ordered' => true, 'type' => 'lower-alpha'],
            'I.' => ['ordered' => true, 'type' => 'upper-roman'],
            'i.' => ['ordered' => true, 'type' => 'lower-roman']
        ];
        $this->inline_tags = [
            '--','~~',"'''","''",'__',',,','^^','{{{'
        ];
    }

일단 new NamuMark(); 를 사용했을 때 실행할 부분들을 추가했다. 지금은 일단 저 부분만 넣어 두기로 했다. 받아온 값을 클래스 멤버에 할당해 놓았다.

 

렌더러를 만들다 보니 개행 미지원 문법을 파싱하는 데 문제가 발생했다. 정규식(PCRE)에서 $를 넣었더니 개행을 인식하지 못하는 것이었다. RAW 텍스트의 개행이 \n으로 되어 있었기 때문이다. 그래서 그 행만 가져오는 함수를 만들었다.

protected function eol($text){
    $e = iconv_strpos($text, "\n");
    return ($e === false)? iconv_strlen($text):$e;
}

protected function parseLine($str){
    $e = explode("\n", $str)[0];
    return ($e === false)? $str:$e;
}

eol 함수는 필요할 것 같아서 일단 만들어 두었다.

 

그리고 parse를 진행하는 함수를 따로 만들었다. 저 for문 안에 내용을 채우면 된다.

public function parse(){
      $this->indentLevel = 0;
      $this->listLevel = 0;
      $this->tokens = [];
      $strlen = iconv_strlen($this->wikitext);
      for($this->pos=0; $this->pos<$strlen; ++$this->pos){
      		// do something......
      }
      return $this->tokens;
  }

  들여쓰기나 리스트 문법을 보면 여러 단계로 이루어진 모습을 볼 수 있다.

이렇게

그래서 그 단계를 $this->indentLevel 등의 변수에다가 저장하려는 것이다.

그리고 이런 데이터를 배열로 만들어서 $this->token 에다 저장하는 것이다.(토큰 생성)

 

그 다음 고려할 점은 문법을 읽어들이는 방식인데, 글자 단위로 읽어들이는 것이 좋을 것 같다.

그래야 개행 지원 문법을 수월하게 파싱할 수 있다.

 redirect 문법

 리다이렉트는 나무마크에서 1순위로 처리하는 문법 중 하나이다. 코드를 최대한 적게 실행하고 다른 페이지로 넘겨주는 게 사용자에게든 서버에게든 좋기 때문이다.

if(preg_match('/^#(redirect|넘겨주기) (.*)$/', $this->parseLine($this->wikitext), $match)){
    array_push($this->tokens, ['name' => 'redirect', 'target' => $match[2]]);
    break;
}

개행 미지원 문법이므로 그 행만 추출해서 파싱한다. 넘겨주기 문법과 패턴이 일치할 경우 토큰을 생성하는 방식이다.

넘겨주기 문법이 나오면 그 이후의 것은 전부 무시되므로 break를 통해 for문에서 빠져나온다.