함수와 OOP(3) - STACK

임쿠쿠·2021년 8월 2일
0

OOP

목록 보기
3/3
post-thumbnail

HTML PARSER

HTML PARSER를 단순화하면, 아래와 같다.

A = <TAG>BODY</TAG>
B = <TAG/>
C = TEXT
const parser = (input) => {
  input = input.trim();
  const result = { name: 'ROOT', type: 'node', children: [] };
  const stack = [{ tag: result }];
  let curr,
    i = 0,
    j = input.length;
  while ((curr = stack.pop())) {
    // 받아온 문자열 끝까지 도는 스캐너
    while (i < j) {
      // i를 직접 조작하면 위험하므로 cursor로 지정
      const cursor = i;
      // i가 0일때 <가 아니면 태그가 아닐 것이다.
      if (input[cursor] === '<') {
        //A, B의 경우
      } else {
        //C의 경우 ~ text
        const idx = input.indexOf('<', cursor);
        // text는 tag의 자식으로 type text 넣는다.
        curr.tag.children.push({
          type: 'text',
          text: input.substring(cursor, idx),
        });
        // < 가 끝나는 지점을 idx로 지정
        i = idx;
      }
    }
  }
  return result;
};

이때, textNode쪽의 역할은 정해지고 다른 알고리즘에 영향을 끼치지 않으므로 함수화 한다. ~ 역할을 인식하면 바로 바꾼다

// textNode는 다른 알고리즘에 영향 안끼치므로 함수로 만듬
const textNode = (input, cursor, curr) => {
  const idx = input.indexOf('<', cursor);
  curr.tag.children.push({
    type: 'text',
    text: input.substring(cursor, idx),
  });
  // 값에 의한 i를 복사했으므로 return을 통해 변경
  return idx;
};

const parser = (input) => {
  input = input.trim();
  const result = { name: 'ROOT', type: 'node', children: [] };
  const stack = [{ tag: result }];
  let curr,
    i = 0,
    j = input.length;
  while ((curr = stack.pop())) {
    while (i < j) {
      const cursor = i;
      if (input[cursor] === '<') {
      } else {
        i = textNode(input, cursor, curr);
      }
    }
  }
  return result;
};
  • 코드를 구성 시 쉬운코드부터 처리한다. ~ 의존성이 낮고 독립된 기능일 가능성이 높기 때문이다.

TEXT가 아닌 TAG의 경우의 수를 고려하여 분기 한다.

(1) <a></a>
(2) <div>~<div>
(3) <img/>
const textNode = (input, cursor, curr) => {...};

const parser = (input) => {
  input = input.trim();
  const result = { name: 'ROOT', type: 'node', children: [] };
  const stack = [{ tag: result }];
  let curr,
    i = 0,
    j = input.length;
  while ((curr = stack.pop())) {
    while (i < j) {
      const cursor = i;
      if (input[cursor] === '<') {
        const idx = input.indexOf('>', cursor);
        i = idx + 1;
        // <a></a> 일 경우
        if (input[cursor + 1] === '/') {
        } else {
          // <img/>인 경우
          // 화이트리스트 작업
          // (1)공통 준비사항
          let name, isClose;
          if (input[idx - 1] === '/') {
            (name = input.substring(cursor + 1, idx - 1)), (isClose = true);
          } else {
            // <div><div>인 경우
            (name = input.substring(cursor + 1, idx)), (isClose = false);
          }
          // (2)공통 처리사항
          const tag = { name, type: 'node', children: [] };
          curr.tag.children.push(tag);
          // isCloser 가 false인 경우 stack에 넣고 back에다가 curr 넣어 놓는다. ~ backpoint 잡는다.
          if (!isClose) {
            stack.push({ tag, back: curr });
            break;
          }
        }
      } else {
        i = textNode(input, cursor, curr);
      }
    }
  }
  return result;
};

elementNode 역할 분리 후 함수화

const textNode = (input, cursor, curr) => {...};

const elementNode = (input, cursor, idx, curr, stack) => {
  let name, isClose;
  if (input[idx - 1] === '/') {
    name = input.substring(cursor + 1, idx - 1);
    isClose = true;
  } else {
    name = input.substring(cursor + 1, idx);
    isClose = false;
  }
  const tag = { name, type: 'node', children: [] };
  curr.tag.children.push(tag);
  if (!isClose) {
    stack.push({ tag, back: curr });
    return true;
  }
  //플래그를 주어 break 실행
  return false;
};
                                           
const parser = (input) => {
  input = input.trim();
  const result = { name: 'ROOT', type: 'node', children: [] };
  const stack = [{ tag: result }];
  let curr,
    i = 0,
    j = input.length;
  while ((curr = stack.pop())) {
    while (i < j) {
      const cursor = i;
      if (input[cursor] === '<') {
        const idx = input.indexOf('>', cursor);
        i = idx + 1;
        if (input[cursor + 1] === '/') {
           curr = curr.back;
        } else {
          if (elementNode(input, cursor, idx, curr, stack)) break;
        }
      } else {
        i = textNode(input, cursor, curr);
      }
    }
  }
  return result;
};

결과

profile
Pay it forward

0개의 댓글