본문 바로가기
연습

프로그래머뇌 청킹연습/표식찾기연습 - 3

by ybs 2022. 5. 25.
반응형

 

[표식찾기 연습]

1단계 코드베이스 선택 

owasp-java-html-sanitizer 에서 HtmlLexer 를 이해하기 위해 필요한 코드들

interface TokenStream {
  HtmlToken next();
  boolean hasNext();
}

abstract class AbstractTokenStream implements TokenStream {
  private HtmlToken tok;

  public final boolean hasNext() {
    if (tok == null) { tok = produce(); }
    return tok != null;
  }

  public HtmlToken next() {
    if (this.tok == null) { this.tok = produce(); }
    HtmlToken t = this.tok;
    if (t == null) { throw new NoSuchElementException(); }
    this.tok = null;
    return t;
  }

  protected abstract HtmlToken produce();
}

final class HtmlLexer extends AbstractTokenStream {
  private final String input;
  private final HtmlInputSplitter splitter;
  private State state = State.OUTSIDE_TAG;

  public HtmlLexer(String input) {
    this.input = input;
    this.splitter = new HtmlInputSplitter(input);
  }

  @Override
  protected HtmlToken produce() { ... }
  
  ...
}

final class HtmlInputSplitter extends AbstractTokenStream {
  @Override
  protected HtmlToken produce() { ... }
  
  ...
}

final class HtmlToken {
  final int start;
  final int end;
  final HtmlTokenType type;

  static HtmlToken instance(int start, int end, HtmlTokenType type) {
    return new HtmlToken(start, end, type);
  }

  boolean tokenInContextMatches(String context, String match) {
    int n = end - start;
    if (n != match.length()) { return false; }
    return context.regionMatches(start, match, 0, n);
  }

  private HtmlToken(int start, int end, HtmlTokenType type) {
    this.start = start;
    this.end = end;
    this.type = type;
  }
}

enum HtmlTokenType {
  ATTRNAME,
  ATTRVALUE,
  QMARKMETA,
  COMMENT,
  DIRECTIVE, // A directive such as a DOCTYPE declaration.
  UNESCAPED,
  QSTRING,
  TAGBEGIN,
  TAGEND,
  TEXT,
  IGNORABLE,
  SERVERCODE,
  ;
}

 

2단계 코드 파악: 선택한 메서드나 함수를 파악하고 코드가 하는 일을 요약

 

전체적인 구조를 다이어그램으로 표현했다. HtmlLex 와 HtmlInputSplitter 둘 다 AbstractTokenStream 을 상속받고 있지만 HtmlInputSplitter 는 HtmlLexer 안에서만 생성되며 사용된다. 왜 그런 계층관계로 표현했을까? 그리고 각각의 produce 가 있게끔 만든 이유가 뭘까?? -> 코드만 보고 왜라는 질문에 답할수 있을까?? 

 

아래 테스트코드를 보면 HtmlLexer 를 통해 HtmlToken 결과를 쉽게 이해할 수 있다.

@Test
public static final void testUrlEndingInSlashOutsideQuotes() {
  assertTokens(
      "<a href=http://foo.com/>Clicky</a>",
      "TAGBEGIN: <a",
      "ATTRNAME: href",
      "ATTRVALUE: http://foo.com/",
      "TAGEND: >",
      "TEXT: Clicky",
      "TAGBEGIN: </a",
      "TAGEND: >");
}

 

디버깅해보니까 아래와 같이 진행된다. 

1. HtmlLexer 의 hasNext 실행

2. HtmlLexer 의 produce 실행되고 

3. readToken 실행하면 내부적으로 splitter hasNext 실행한다.

4. HtmlInputSplittor 의 produce 실행

5. paseToken 실행

6. HtmlToken 결과리턴 ex) start:0 end:2 type: TAGBEGIN . <b>을 의미

 

 

3단계 사용하는 표식의 적극적 확인

설명 : 읽는 도중 '아, 그렇구나'라는 생각이 들면서 그 코드의 의미를 좀 더 이해하게 되면 잠시 멈추고 왜 그렇게 생각했는지를 적어보라. 주석문, 변수명, 메서드명, 임시 저장값 등 어느 것이든 표식이 될 수 있다.

 

1. 추상 메서드 produce 를 네이밍으로 HtmlToken 객체를 만드는 역할임을 알 수 있다.

2. HtmlLexer.produce -> readToken -> HtmlInputSplittor.produce -> parseToken 순서로 작업되는 것을 알 수 있다. 처음에 readToken 을 수행하지만 값이 없으면 parseToken 을 수행한다.

 

4단계 회고

- 어떤 표식을 찾았는가?

HtmlLexer, readToken, parseToken, produce

 

- 찾은 표식들은 코드의 요소인가, 아니면 사람의 언어로 된 정보인가?

둘다.

 

- 그 표식들은 무엇에 관해 알려주고 있는가?

html 문자열을 파싱해 HtmlToken 객체에 옮긴다. 루프를 통해 하나씩 의미있는 단위로 파싱하고 이후 분석을 진행한다.

 

- 그 표식들은 코드의 도메인에 대한 지식을 나타내는가?

 

- 그 표식들은 코드의 기능에 대한 지식을 나타내는가?

 

[청킹연습]

1단계 코드 선정

interface TokenStream {
  HtmlToken next();
  boolean hasNext();
}

abstract class AbstractTokenStream implements TokenStream {
  private HtmlToken tok;

  public final boolean hasNext() {
    if (tok == null) { tok = produce(); }
    return tok != null;
  }

  public HtmlToken next() {
    if (this.tok == null) { this.tok = produce(); }
    HtmlToken t = this.tok;
    if (t == null) { throw new NoSuchElementException(); }
    this.tok = null;
    return t;
  }

  protected abstract HtmlToken produce();
}

final class HtmlLexer extends AbstractTokenStream {
  private final String input;
  private final HtmlInputSplitter splitter;
  private State state = State.OUTSIDE_TAG;

  public HtmlLexer(String input) {
    this.input = input;
    this.splitter = new HtmlInputSplitter(input);
  }

  @Override
  protected HtmlToken produce() { ... }
  
  ...
}

final class HtmlInputSplitter extends AbstractTokenStream {
  @Override
  protected HtmlToken produce() { ... }
  
  ...
}

2단계 코드 파악

설명: 최대 2분을 넘지 않도록 타이머를 설정하고 코드 파악. 시간이 다 되면 코드는 보지 않는다.

3단계 코드 재현

설명: 기억을 되살려 새롭게 코드를 다시 작성

interface TokenStream {
  HtmlToken next();
  boolean hasNext();
}

abstract class AbstractTokenStream implements TokenStream {

  final HtmlToken tok;
  
  @Override
  public HtmlToken next() {
    if (this.tok == null) { this.tok = produce(); }
  }

  @Override
  public boolean hasNext() {
    if (this.tok == null) { this.tok = produce(); }  
  }

  protected abstract HtmlToken produce();

}

final class HtmlLexer extends AbstractTokenStream {
  HtmlInputSplitter splitter;
  State state = State.OUTSIDE_TAG

  @Override
  public HtmlToken produce() {
  }
}

final class HtmlInputSplitter extends AbstractTokenStream {

  @Override
  public HtmlToken produce() {
  }
}

4단계 회고

- 어느 부분을 쉽게 기억했는가?

전체적인 구조. 

 

- 부분적으로 기억한 코드가 있는가?

next, hasNext 구현부분

 

- 전체를 다 기억하지 못한 코드가 있는가?

없음

 

- 기억하지 못한 라인들이 있다면 그 이유가 무엇일까?

 

 

- 기억하지 못한 라인에 본인이 익숙하지 않은 프로그래밍 개념이 들어 있지는 않는가?

놉 시간부족

 

- 기억하지 못한 라인에 본인이 익숙하지 않은 도메인 지식이 있지는 않는가?

놉 시간부족

 

반응형