2012-07-23 3 views
7

JavaScript 소스 파일을 구문 분석하고, 사실을 추출하고, 코드의 일부를 삽입/대체해야하는 프로그램을 작성하고 있습니다. 내가해야 할 것 사물의 종류의 간단한 설명이 코드 주어진다 :ANTLR을 사용하여 소스 코드를 분석하고 수정합니다. 내가 잘못하고있는거야?

foo(['a', 'b', 'c']); 

추출 'a', 'b''c'과 같은 코드를 다시 작성 : 내가 사용하고

foo('bar', [0, 1, 2]); 

내 구문 분석을위한 ANTLR, C# 3 코드 생성. 다른 누군가는 이미 자바 스크립트 문법에 기여했습니다. 소스 코드의 파싱이 진행 중입니다.

내가 겪고있는 문제는 실제로 소스 파일을 올바르게 분석하고 수정하는 방법을 알아내는 것입니다. 실제로 문제를 해결하려고 시도하는 각각의 접근 방식은 막 다른 길로 인도합니다. 내가 도울 수 있지만 그 의도대로 도구를 사용하지 않거나 AST를 다루는 데있어 너무 초보자라고 생각하지 않습니다.

첫 번째 방법은 TokenRewriteStream을 사용하여 구문 분석하고 내가 관심있는 규칙의 부분 방법을 구현하는 것입니다.이 방법이 토큰 스트림을 매우 쉽게 수정할 수있는 것으로 보이지만 분석을위한 컨텍스트 정보가 충분하지 않습니다. 내가 접근 할 수있는 것은 코드의 전체 구조에 대해 충분히 말해주지 않는 토큰의 플랫 스트림 인 것 같습니다.

a.b.foo(); 

이 좀 더 정교한 코드 분석을 할 수 있도록하기 위해, 내 두 번째 방법 : 예를 들어, 단순히 그 것 또한 거짓 일치하기 때문에 작동하지 않을 첫 번째 토큰에서 찾고 foo 함수가 호출되고 있는지 여부를 감지하는 더 많은 트리를 만들기 위해 다시 쓰기 규칙으로 문법을 수정하는 것이 었습니다. 이제 첫 번째 샘플 코드 블록은 다음을 생성합니다.

 
Program 
    CallExpression 
     Identifier('foo') 
     ArgumentList 
      ArrayLiteral 
       StringLiteral('a') 
       StringLiteral('b') 
       StringLiteral('c') 

코드를 분석하는 데 매우 효과적입니다. 그러나 이제 코드를 쉽게 다시 작성할 수 없습니다. 물론, 원하는 코드를 나타 내기 위해 트리 구조를 수정할 수는 있지만 소스 코드를 출력하는 데 사용할 수는 없습니다. 각 노드와 관련된 토큰이 원본 텍스트에서 수정해야 할 위치를 알 수있는 충분한 정보를 얻을 수 있기를 기대했지만 토큰 색인이나 행/열 번호 만 있으면됩니다. 줄 번호와 열 번호를 사용하려면 소스 코드를 두 번째 통과해야합니다.

ANTLR을 올바르게 사용하여 필요한 것을 수행하는 방법을 이해하지 못하는 것 같습니다. 이 문제를 해결할 더 적절한 방법이 있습니까?

+0

* "이 문제를 해결하는 더 적절한 방법이 있습니까?" 당신은 당신의 입력을 분석하고 조작 한 다음 직접 출력합니다. StringTemplate, Dave가 언급했듯이이를 도와 줄 수 있습니다. –

답변

1

그래서 실제로는 다시 트리 문법을 사용하고 TokenRewriteStream을 사용하여 토큰을 삽입/교체 할 수 있습니다. 게다가, 실제로 그렇게하기 쉽습니다. 내 코드는 다음과 유사합니다.

var charStream = new ANTLRInputStream(stream); 
var lexer = new JavaScriptLexer(charStream); 
var tokenStream = new TokenRewriteStream(lexer); 
var parser = new JavaScriptParser(tokenStream); 
var program = parser.program().Tree as Program; 

var dependencies = new List<IModule>(); 

var functionCall = (
    from callExpression in program.Children.OfType<CallExpression>() 
    where callExpression.Children[0].Text == "foo" 
    select callExpression 
).Single(); 
var argList = functionCall.Children[1] as ArgumentList; 
var array = argList.Children[0] as ArrayLiteral; 

tokenStream.InsertAfter(argList.Token.TokenIndex, "'bar', "); 
for (var i = 0; i < array.Children.Count(); i++) 
{ 
    tokenStream.Replace(
     (array.Children[i] as StringLiteral).Token.TokenIndex, 
     i.ToString()); 
} 

var rewrittenCode = tokenStream.ToString(); 
2

string template 라이브러리를 보았습니까? 같은 사람이 ANTLR을 작성했으며 함께 작업하는 것이 목적입니다. 그것은 당신의 찾고 즉 무엇을 할 것이 맞을 것 같습니다. 출력 된 문법 규칙을 형식화 된 텍스트로 출력합니다. 당신이 뭘 하려는지

Here is an article on translation via ANTLR

+0

자바 스크립트 코드의 일부를 주입하고 교체해야 할 때 전체 자바 스크립트 프로그램을 출력하는 코드를 작성하지 않아야합니다. 그러나 전체 출력 엔진을 만들 필요가 있다면 String Template이 유망 해 보입니다. 도움말 링크를 제공해 주셔서 감사합니다. – Jacob

6

은 서로 하나 개의 프로그램의 자동 생성, 즉, program transformation라고합니다. 당신이 "잘못"하고있는 것은 파서가 당신이 필요로하는 모든 것이고, 그것이 아니라는 것과 파서를 채워야한다는 것을 가정하는 것입니다. 이 잘 (하는 AST를 구축하기 위해) 파서가 그렇게

도구는하는 AST (절차 및 패턴 감독 모두), 및 prettyprinters 법적 소스 코드에 다시 (수정) AST를 변환을 수정하는 것을 의미합니다.당신은 ANTLR이 prettyprinter와 함께 오지 않는다는 사실에 고심하고있는 것처럼 보입니다. 그것은 그것의 철학의 일부가 아닙니다. ANTLR은 (파인) 파서 생성기입니다. 다른 대답은 ANTLR의 "문자열 템플릿"을 사용하는 것이 좋습니다.이 템플릿은 자체적으로 prettyprinters가 아니지만이를 구현하는 데 사용할 수 있습니다. 이것은 보이는 것보다 더 힘든 일입니다. 내 대답은 compiling an AST back to source code에 나와 있습니다.

여기 실제 문제는 거짓 "파서가 있으면 복잡한 프로그램 분석 및 변환 도구를 구축하는 중입니다."라고 가정합니다. 이에 대한 오랜 토론은 내 수필 인 Life After Parsing을 참조하십시오. 기본적으로 작업을 수행하는 대신 인프라의 상당 부분을 직접 재구성하지 않는 한 파서가이를 수행하는 툴링이 더 많이 필요합니다. 실용적인 프로그램 변환 시스템의 다른 유용한 기능으로는 일반적으로 소스에서 소스로의 변환이 포함되며, 이는 나무에서 복잡한 패턴을 찾아 교체하는 문제를 상당히 단순화합니다. 예를 들어

, 당신은 우리의 도구의 소스 - 소스 변환 기능을 (한 경우 DMS Software Reengineering Toolkit, 당신은 당신의 예제 코드의 일부를 쓸 수있을 거라고 변경이 DMS를 사용하여 변환 :

 domain ECMAScript. 

     tag replace; -- says this is a special kind of temporary tree 


     rule barize(function_name:IDENTIFIER,list:expression_list,b:body): 
      expression->expression 
     = " \function_name ('[' \list ']') " 
     -> "\function_name(\firstarg\(\function_name\), \replace\(\list\))"; 


     rule replace_unit_list(s:character_literal): 
      expression_list -> expression_list 
      replace(s) -> compute_index_for(s); 

     rule replace_long_list(s:character_list, list:expression_list): 
      expression_list -> expression_list 
      "\replace\(\s\,\list)-> "compute_index_for\(\s\),\list"; 

규칙 "외부"메타 "프로 시저"first_arg "(식별자를"foo "[당신이 이것을하고 싶다고 추측하고있는]"bar "를 계산하는 방법을 알고 있음) 및 문자열 리터럴을 가진"compute_index_for "는 정수로 대체하려면

개별 다시 쓰기 규칙에는 하위 트리를 나타내는 슬롯이 "(....)"인 매개 변수 목록 " 이름이 붙은 패턴, 일치 할 패턴으로 동작하는 왼편, 대체물로 동작하는 오른편이 있습니다. 보통 메타쿼트로 인용 부호 ""이 사용됩니다.이 문자는 대상 언어의 재 작성 규칙 언어 텍스트를 분리합니다. JavaScript) 텍스트. 특별 재 작성 규칙 언어 항목을 나타내는 metaquotes 안에는 많은 메타 이스케이프 **가 있습니다. 일반적으로 이것은 매개 변수 이름이며 매개 변수가 나타내는 모든 유형의 이름 트리를 나타내거나 외부 메타 프로 시저 호출을 나타냅니다 (예 : first_arg, 해당 인수 목록 (,)은 메타 인용 됨)! 또는 마지막으로 " "바꾸기"와 같은 "태그"를 사용하여 더 많은 변환 작업을 수행 할 미래의 의도를 나타내는 독특한 트리입니다.

이 특정 규칙 집합은 후보 함수 호출을 금지 된 버전으로 대체하고 추가 의도는 "바꾸기"로 변경하여 목록을 변환합니다. 다른 두 가지 변환은 목록의 요소를 한 번에 하나씩 처리하여 "대체"를 변환하고 마지막으로 끝에 떨어져 교체가 완료 될 때까지 목록 아래로 교체를 밀어 넣어 의도를 실현합니다. (이것은 루프의 변형 동등 물입니다.)

세부 정보가 정확하지 않으므로 구체적인 예가 다소 다를 수 있습니다.

이러한 규칙을 적용하여 구문 분석 된 트리를 수정하면 DMS가 결과를 미리 미리 인쇄 할 수 있습니다 (일부 구성의 기본 동작은 "AST 구문 분석, 고갈 될 때까지 규칙 적용, prettyprint AST"입니다).

(High School) Algebra as a DMS domain에 "언어 정의", "다시 쓰기 규칙 정의", "규칙 및 미리 인쇄 적용"의 전체 과정을 볼 수 있습니다.

다른 프로그램 변환 시스템에는 TXLStratego이 포함됩니다. 우리는 DMS가 이들의 산업 강도 버전이라고 상상합니다. 여기서 우리는 many standard language parsers and prettyprinters을 포함한 모든 인프라를 구축했습니다.

+0

자세한 답변 해 주셔서 감사합니다. 나는 예쁜 프린터 전체를 쓰지 않아도되기를 바랬다. (또는 DMS의 관점에서 보면 충실한 프린터가 필요할 것이다.) 나는 단지 코드의 일부분을 주입/대체하려고하기 때문에 이것을하기 위해 전체 프로그램을 출력하는 코드를 작성해야한다면 불행 할 것이다. – Jacob

+0

나는 일을 피하고자하는 욕구를 이해한다 : - 나쁜 소식 : 코드를 변경하고자 할 때 매우 어렵다. 좋은 소식 : 누군가가 모든 필수 요소를 다했습니다. –