티스토리 뷰

SW개발/ANTLR

visitor 사용

개소왕 2018. 5. 30. 09:11

* 이클립스에서 비지터 사용하기

- 기본적으로 비지터 생성되지 않음

(no-visitor 옵션)

 

-  프로젝트->우클릭>Properties > ANTLR > Enable project specific setting

(프로젝트에 ANTLR 설정 적용 안 된 경우)

 

- 비지터 켜기 위해선

> tool Generate parse tree visitors 체크하면

ANTLR 실행시 -visitor 옵션 주는것과 같음

- 참고: External... 로 실행할 때 -no-visitor 만 빼면 되는게 아니라, -visitor 를 꼭 넣어줘야 함

 

* 컴파일 하면 (문법 이름이 Abc 라면)

AbcBaseVisitor

AbcVisitor 가 생김

 

 

* 사용 방법

AbcVisitor 를 상속한 후 

 

visit얼터너티브이름(얼터너티브이름Context) 이란 메서드를 오버라이딩 함.

 

 

 

* ctx.토큰이름() or ctx.룰이름() 사용 가능

 

visitXX() return 값이 해당 얼터너티브의 치환값이 됨.

 

 

 

* 참고 : 비지터는 서브트리 탐색 위해서 명시적으로 visit() 를 호출해야 한다

안 하면 누락

(물론 AbcVisitor 에 다 구현되어 있긴 함)

 

 

 

[ Visitor 선언 및 활용 ]


* 예제 문법 Hello

 

grammar Hello;

r  : 'hello' myId ;

myId : ID;

 

 

 

ID : [a-z]+ ; 

 

WS : [ \t\r\n]+ -> skip ;

 

기본 문법에서 myId 룰 추가함.. (child 테스트 하려고..)

 

 

 

* Visitor 클래스 선언 코드

 

문법 'Hello'를 선언했으므로

인터페이스 HelloVisiitor 와 

이를 구현한 클래스 HelloBaseVisitor 가 기본으로 생성된다

 

이를 상속한 HelloMyVisitor 를 정의해보면....

public class HelloMyVisitor extends HelloBaseVisitor<Integer> {

 

@Override public Integer visitR(@NotNull HelloParser.RContext ctx) {

return visitChildren(ctx);

 

 

}

 

@Override public Integer visitMyId(@NotNull HelloParser.MyIdContext ctx) {

System.out.println("visitMyId() " + ctx.getText());

return visitChildren(ctx); 

}

}

 

반환값 Integer 는 임의로 지정한것.. (사용자가 임의 지정할 수 있음)

HelloBaseVisitor 를 상속하면 따로 구현할 메서드는 없지만,

visitr룰이름() 메서드를 오버라이드할 수 있음.

 

 

 

* Visitor 사용 코드

// 파스 트리 생성

String s = "hello abc";

 

HelloLexer lex = new HelloLexer(CharStreams.fromString(s));

CommonTokenStream cts = new CommonTokenStream(lex);

HelloParser par = new HelloParser(cts);

ParseTree pt = par.r();

 

// 비지터 호출

HelloMyVisitor v = new HelloMyVisitor();

v.visit(pt);

 

 

 

 

- 리스너에서도 있었던 Lexer 선언, Parser 선언, ParserTree 생성 그대로 있음.

다만 리스너는 ParseTreeWalker 에 리스너 넣어서 사용했지만,

비지터는 직접 v.visit(ParserTree) 로 직접 호출함

 

- 리스너와 동일하게 HelloParser.룰이름Context 를 파라메터로 받음.

 

- 참고 : Visitor 가 직접 파싱하는게 아님.. 이미 생성된 ParseTree 를 순회하는 역할만 함

 

- visit룰() 메서드 언제나 visitChildren(ctx) 메서드를 호출하도록 되어있음.

안 하면 하위노드를 순회하지 않음.

순회하지 않지만 ctx.getChild() 로 값을 가져올 순 있음.

(이미 파싱은 되어있고, 순회만 하는거니까.)

 

- 리스너는 enter룰이름() , exit룰이름() 이 존재하지만 

비지터는 visit룰이름() 하나만 있음.

 

 

- 만약 "hello 123" 이란 텍스트를 집어넣는다면

ID 규칙이 <영어 소문자> 로만 정의 되어있어서 문법에 맞지 않음.

<룰 myId> 는 방문할 수 없을 것 같지만

 

visit() 호출 => visit(R) 호출 -> visitMyId() 호출은 정상적으로 일어남.

다만 해당 노드의 값은 <어휘 ID>의 규칙을 만족하지 못해서 <missing ID> 로 표시됨.

 

 

- 정리하면 visit룰이름() 메서드는 노드 방문시 호출되며, 

visitChild() 을 직접 호출해줘야 하위 노드를 방문할 수 있고,

문법을 틀려 파싱에 실패한 노드도 호출됨.

 

 

[[ 기타

* 왜 기본값이 no-visitor 일까?

j/ ??

'SW개발 > ANTLR' 카테고리의 다른 글

Listener , Visitor 비교  (0) 2018.07.27
ANTLRInputsream DEPRECATED  (0) 2018.06.16
ANTLR Hello 예제  (0) 2018.05.21
antlr ide Package 지정  (0) 2017.09.25
ANTLR - IDE 설치, 프로젝트시작 / Eclipse  (0) 2017.09.21
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함