컴파일러 작성 컴파일러-사용 및 기능에 대한 이해 그런 다음 DFA를 사용하여 순방향 경로를 식별하는

이것은 언어 프로젝트에 사용 된 개념을 프레임 워크 형태로 추상화하는 것을 목표로하는 추상화 프로젝트의 자매 프로젝트에 중점을 둔 일련의 질문의 일부입니다. 자매 프로젝트는 OILexer라고하며 일치하는 코드 삽입을 사용하지 않고 문법 파일에서 구문 분석기를 구성합니다.

구조 입력과 관련하여 다음 질문에 관련된 일부 다른 페이지는 볼 수 있습니다 여기에 , 사용의 용이성, 발견 여기에 . 프레임 워크에 대한 문의 및 적절한 게시 장소와 관련된 메타 주제는 여기 에서 찾을 수 있습니다 .

주어진 문법에서 구문 분석 트리를 추출하기 시작한 시점에 도달했습니다. 그런 다음 DFA를 사용하여 순방향 경로를 식별하는 재귀 하강 파서가 나옵니다 (ANTLR 4의 LL (*)과 유사). 통찰력을 얻기 위해 그것을 열 것이라고 생각했습니다.

파서 컴파일러에서 어떤 종류의 기능이 이상적입니까?

지금까지 구현 된 내용에 대한 간략한 개요는 다음과 같습니다.

  1. 템플릿
  2. 주어진 시점에서 무엇이 유효한지 미리 예측해보십시오.
  3. ‘리터럴 화’규칙은 규칙 내에서 리터럴을 가져 와서 어떤 토큰을 사용했는지 확인합니다.
  4. 비 결정적 오토마타
  5. 결정 론적 오토마타
  6. 토큰 인식을위한 간단한 어휘 상태 머신
  7. 토큰 자동화 방법 :
    • 스캔-주석에 유용합니다. 주석 : = “/ *”Scan ( “* /”);
    • 빼기-식별자에 유용합니다. 식별자 : = 빼기 (IdentifierBody, Keyword);
      • 식별자가 키워드를 허용하지 않도록합니다.
    • 인코딩-기본 N 전환의 시리즈 X 카운트로 자동화를 인코딩합니다.
      • UnicodeEscape : = “\\ u”BaseEncode (IdentifierCharNoEscape, 16, 4);
        • 16 진수 4 전환을 사용하여 유니 코드 이스케이프를 16 진수로 만듭니다. 이 코드와 [0-9A-Fa-f] {4}의 차이점은 Encode를 사용한 자동화 결과로, 허용 된 16 진수 값 세트가 IdentifierCharNoEscape 범위로 제한됩니다. 따라서 \ u005c를 지정하면 인코딩 버전이 값을 허용하지 않습니다. 이와 같은 것들에는 심각한 경고가 있습니다. 결과 자동화는 상당히 복잡 할 수 있습니다.

구현되지 않은 것은 CST 생성입니다.이 작업을 수행하기 위해 적절한 컨텍스트를 전달하도록 결정적 자동화를 조정해야합니다.

관심있는 사람이라면 누구나 원래 형식의 T * y♯ 프로젝트를 꽤나 올렸습니다 . 각 파일은 다른 모든 파일에 연결되어야하며 개별 규칙에서 연결하기 시작하여 너무 오래 걸렸지 만 (자동화하기가 더 쉬웠을 것입니다!)

더 많은 컨텍스트가 필요한 경우 그에 따라 게시하십시오.

편집 5-14-2013 : 주어진 언어 내에서 상태 시스템에 대한 GraphViz 그래프를 작성하는 코드를 작성했습니다. AssemblyPart의 GraphViz digraph는 다음과 같습니다 . 언어 설명에 링크 된 멤버는 상대 규칙에 해당 규칙의 digraph와 함께 rulename.txt가 있어야합니다. 예제를 게시 한 후 일부 언어 설명이 변경되었습니다. 이는 문법에 대한 사항을 단순화했기 때문입니다. 흥미로운 graphviz 이미지가 있습니다.



답변

이것은 훌륭한 질문입니다.

최근에 많은 파싱 작업을 해왔으며 IMHO의 주요 기능 중 일부는 다음과 같습니다.

  • 프로그래밍 방식의 API-라이브러리를 가져 오기만하면 프로그래밍 언어 내에서 사용할 수 있습니다. GUI 또는 BNF와 같은 인터페이스도 가질 수 있지만 툴링, IDE, 정적 분석, 테스트, 언어 추상화 기능, 프로그래머 친숙성, 문서 생성기, 빌드 프로세스, 또한 작은 파서를 사용하여 대화식으로 재생할 수 있으므로 학습 곡선이 크게 줄어 듭니다. 이러한 이유로 인해 “중요 기능”목록의 맨 위에 배치됩니다.

  • @guysherman이 언급했듯이 오류보고. 오류가 발견되면 오류가 발생한 위치와 오류가 발생한 시점을 알고 싶습니다. 불행히도, 나는 역 추적을 할 때 적절한 오류를 생성하는 방법을 설명하는 좋은 자료를 찾지 못했습니다. (아래 @ Sk-logic의 의견에 유의하십시오).

  • 부분 결과. 구문 분석이 실패하면 오류 위치 이전의 입력 부분에서 구문 분석 된 내용을 볼 수 있기를 원합니다.

  • 추출. 충분한 기능을 구축 할 수 없으며 사용자는 항상 더 많은 기능이 필요하므로 가능한 모든 기능을 미리 알아내는 것은 실패로 끝납니다. 이것이 템플릿의 의미입니까?

  • 나는 당신의 # 2 (미리 예측)에 동의합니다. 좋은 오류 보고서를 생성하는 데 도움이된다고 생각합니다. 다른 것에 유용합니까?

  • 구문 분석이 발생할 때 구문 분석 트리 작성 지원 :

    • 구체적인 구문 트리는 트리의 구조가 문법에 직접 대응하며 이후 단계의 오류보고를위한 레이아웃 정보를 포함합니다. 이 경우 클라이언트는 올바른 트리 구조를 얻기 위해 아무 것도 하지 않아도됩니다 . 문법에 직접 의존해야합니다.
    • 추상 구문 트리 이 경우 사용자는 모든 파싱 트리를 사용하여 바이올린을 칠 수 있습니다.
  • 어떤 종류의 로깅. 나는 이것에 대해 확신하지 못한다. 구문 분석기가 시도한 규칙의 추적을 표시하거나 공백 또는 주석과 같은 정크 토큰을 추적하기 위해 (예를 들어) 토큰을 사용하여 HTML 문서를 생성하려는 경우가 있습니다.

  • 상황에 맞는 언어를 처리하는 능력. 이 언어가 얼마나 중요한지 확실하지 않습니다. 실제로 언어의 수퍼 세트를 컨텍스트가없는 문법으로 구문 분석 한 다음 나중에 추가 패스에서 컨텍스트 감지 제약 조건을 확인하는 것이 더 깔끔해 보입니다.

  • 특정 상황에서 오류 보고서를 조정하고 문제를보다 신속하게 이해하고 수정할 수 있도록 사용자 정의 오류 메시지.

다른 한편으로, 현재 진행 상황에 대해 최신 정보는 아니지만 오류 수정이 특히 중요하지 않습니다. 내가 목격 한 문제는 도구가 제공하는 잠재적 인 수정 사항이 1) 너무 많고 2) 실제 실수와 일치하지 않으므로 그다지 도움이되지 않는다는 것입니다. 이 상황이 개선되기를 바랍니다 (또는 이미 그렇게했을 것입니다).


답변

나는 언어 디자인에 경험이 없지만 게임 엔진을 만들 때 IDE를 만들 때 한 번 파서를 작성했습니다.

제 생각에는 최종 사용자에게 중요한 것은 말이되는 오류 메시지입니다. 특히 지구를 산산조각내는 것은 아니지만, 거꾸로 따라 가면 이것의 주요 의미 중 하나는 오 탐지를 피할 수 있어야한다는 것입니다. 거짓 긍정은 어디에서 오는가? 파서는 첫 번째 오류에서 넘어져서 완전히 회복되지 않습니다. C / C ++는 이것으로 유명합니다 (최신 컴파일러는 조금 더 똑똑하지만).

그래서 대신에 무엇이 필요합니까? 나는 어떤 시점에서 유효하지 않은 / 무효를 아는 것보다는 유효하지 않은 것을 가져 와서 최소한으로 변경하여 유효하게 만들기 위해 최소한의 변경을해야한다고 생각합니다. 당신의 재귀 하강에 혼란스러워. 이 정보를 생성 할 수있는 파서를 만들 수 있으면 매우 강력한 파서를 얻을 수있을뿐만 아니라 파서를 사용하는 소프트웨어에 대한 환상적인 기능이 열립니다.

나는 이것이 사실이라면 정말 어려우거나 어리석은 것을 암시 할 수 있음을 알고 있습니다. 이것이 당신이 찾고있는 종류가 아닌 경우, 나는 행복하게 답변을 삭제합니다.


답변

문법에는 “재귀 규칙을 남기지 마십시오”와 같은 제한이 없어야합니다. 오늘날 널리 사용되는 도구가 이것을 가지고 있으며 yacc 가 올바르게 수행 한지 거의 50 년이 지나서 LL 문법을 빨아들이는 것만 이해할 수 있다는 것은 우스운 일입니다 .

올바른 재귀에 대한 예 (yacc 구문 사용) :

list:
      elem                  { $$ = singleton($1); }
    | elem ',' list         { $$ = cons($1, $2);  }
    ;

왼쪽 재귀의 예 (yacc synatx 사용) :

funapp:
    term                    { $$ = $1; }
    | funapp term           { $$ = application($1, $2); }
    ;

이제 이것은 다른 것으로 “리팩토링”될 수 있지만 두 경우 모두 특정 종류의 재귀는 이것을 작성하는 “올바른”방법입니다. (예제 언어에서) 목록은 재귀 적이지만 함수 응용 프로그램은 재귀 적입니다. .

고급 도구를 사용하면 도구가 하나에 부과하는 모든 것을 왼쪽 / 오른쪽 재귀로 “리팩토링”하는 대신 자연스럽게 물건을 쓸 수있는 방법을 지원한다고 기대할 수 있습니다.