이 유형의 파서의 이름 또는 존재하지 않는 이유


27

기존 파서는 전체 입력을 소비하고 단일 파싱 트리를 생성합니다. 연속 스트림을 소비하고 구문 분석 포리스트를 생성하는 스트림을 찾고 있습니다 [ 편집 :이 용어를 사용하는 것이 왜 전통적이지 않은지에 대한 의견의 토론 참조 ]. 내 직감은 내가 그런 파서 를 필요로하는 (또는 내가 필요 하다고 생각할 수있는) 사람이 될 수 없다고 말하지만 몇 달 동안 수색을 시도했지만 아무 소용이 없었다.

본인은 XY 문제로 인해 혼란에 빠질 수 있음을 알고 있습니다. 나의 궁극적 인 목적은 대부분의 텍스트를 무시하고 텍스트 스트림을 구문 분석하고 인식되는 섹션에서 구문 분석 트리 스트림을 생성하는 것입니다.

따라서 내 질문은 조건부입니다. 이러한 특성을 가진 파서 클래스가 있으면 무엇이라고합니까? 그렇지 않다면 왜 그렇지 않습니까? 대안은 무엇입니까? 아마도 기존 파서가 원하는 것을 수행 할 수있는 방법이 없습니다.


1
기본적으로 파서는 단일 문서를 구문 분석하고 구문 분석 트리를 생성 한 다음 즉시 다른 문서 구문 분석을 시작합니다.이 동작 수정이 단일 문서에 적용된 다양한 구문 분석 기술과 비교하여 사소한 것으로 생각합니다. 따라서 특별한 용어가 부족합니다.
9000

3
"Parse Forest"에 대한 Google 검색을 수행하고 Earley Parser가이를 생성 한다는 것을 발견 했습니다.
Robert Harvey

7
모나 딕 파서 콤비 네이터를 찾고 계십니까? 즉, 여러 개의 작은 파서로 구성된 더 큰 파서입니다. 한 언어의 "섬"이 다른 언어에 포함 된 상황에 유용합니다. :는 C # 디자인 팀 루크 호반에 내 옛 동료가 그들에 좋은 기사가 blogs.msdn.com/b/lukeh/archive/2007/08/19/...
에릭 Lippert의

3
약간의 혼란이 있습니다. 스트림의 각 문서에 대해 구문 분석 트리를 원하고 함께 구문 분석 포리스트를 구성한다는 의미입니까? 이는 파싱 포리스트의 일반적인 의미가 아닙니다. 구문 분석 포리스트는 여러 가지 방법으로 구문 분석 할 수있는 하나의 모호한 문서 (약간 단순화)에 대한 구문 분석 트리 집합입니다. 이것이 바로 모든 답변에 관한 것입니다. 스트림이 가비지로 분리 된 많은 완전한 문서로 구성되어 있습니까, 아니면 부분적으로 왜곡 된 단일 문서입니까? 문서가 구문 적으로 정확해야합니까? 적절한 기술 답변은 그것에 달려 있습니다.
babou

1
그런 다음 구문 분석 포리스트와 Earley, GLR, Marpa, 파생 상품에 대한 모든 대답을 잊어 버리십시오. 그들은 다른 이유가 나타나지 않으면 분명히 당신이 원하는 것이 아닙니다. 문서가 구문 적으로 정확합니까? 일부 구문 분석 기술은 부분적으로 왜곡 된 문서에 대한 컨텍스트를 다시 만들 수 있습니다. 이 문서들에 대한 정확한 구문이 있습니까? 모두 같은 것입니까? 구문 분석 트리를 정말로 원하십니까, 아니면 문서를 분리하여 나중에 만족시킬 것 같습니까? 나는 당신의 처리를 개선시킬 수있는 것을 알고 있다고 생각하지만, 당신이 그것을 선반에서 얻을 수 있는지 확실하지 않습니다.
babou

답변:


48

전체 입력이 소비되기 전에 (부분) 결과를 반환하는 파서를 증분 파서 라고합니다 . 입력에 나중에 결정되는 문법에 로컬 모호성이있는 경우 증분 구문 분석이 어려울 수 있습니다. 또 다른 어려움은 아직 도달하지 않은 구문 분석 트리 부분을 무시하는 것입니다.

가능한 모든 구문 분석 트리의 포리스트를 반환하는 파서, 즉 모호한 문법을 ​​도출 할 때마다 구문 분석 트리를 반환하는 파서를 호출합니다. 이러한 이름이 아직 있는지 확실하지 않습니다. 나는 알고 Marpa 파서 생성기 이 할 수 있지만, 어떤 Earley 또는 GLR 기반 파서는이 해낼 수 있어야합니다.


그러나, 당신은 그 어느 것도 원하지 않는 것 같습니다. 여러 개의 포함 된 문서가 있고 다음 사이에 쓰레기가있는 스트림이 있습니다.

 garbagegarbage{key:42}garbagegarbage[1,2,3]{id:0}garbage...

가비지를 건너 뛰고 각 문서에 대해 AST 시퀀스를 생성하는 파서가 필요합니다. 이것은 가장 일반적인 의미에서 증분 파서로 간주 될 수 있습니다. 그러나 실제로 다음과 같은 루프를 구현합니다.

while stream is not empty:
  try:
    yield parse_document(stream at current position)
  except:
    advance position in stream by 1 character or token

그러면 parse_docment함수는 기존의 비 증분 파서가됩니다. 성공적인 구문 분석을 위해 입력 스트림을 충분히 읽었는지 확인하는 데 약간의 어려움이 있습니다. 이를 처리하는 방법은 사용중인 파서 유형에 따라 다릅니다. 가능한 구문 분석 오류에서 버퍼를 늘리거나 지연 토큰 화를 사용할 수 있습니다.

게으른 토큰 화는 아마도 입력 스트림으로 인해 가장 우아한 솔루션 일 것입니다. 렉서 단계에서 고정 된 토큰 목록을 생성하는 대신 파서는 렉서 콜백에서 다음 토큰을 느리게 요청합니다 [1] . 그런 다음 어휘 분석기는 필요한만큼 많은 스트림을 소비합니다. 이런 식으로, 파서는 스트림의 실제 끝에 도달하거나 실제 구문 분석 오류가 발생한 경우에만 실패 할 수 있습니다 (즉, 가비지 상태에서 구문 분석을 시작했습니다).

[1] 콜백 기반 어휘 분석기는 다른 상황에서도 좋은 생각이다. 왜냐하면 가장 긴 토큰 매칭과 관련된 문제를 피할 수 있기 때문이다 .

어떤 종류의 문서를 검색하고 있는지 알고 있다면 건너 뛰기를 최적화하여 유망한 위치에서만 중지 할 수 있습니다. 예를 들어 JSON 문서는 항상 문자 {또는로 시작합니다 [. 따라서 가비지는 이러한 문자를 포함하지 않는 문자열입니다.


5
귀하의 의사 코드는 실제로 내가하고있는 일이지만 그게 추악한 해킹이라고 생각했습니다. 파서는 두 가지 종류의 예외 ( NO_MATCHUNDERFLOW)를 발생 시켜 스트림 위치를 전진 시킬지 또는 더 많은 입력을 기다려야하는지 구분할 수 있습니다.
Kevin Krumwiede

5
@Kevin : 독점적 인 형식으로 네트워크에서 들어오는 데이터를 처리하기 위해 일부 안전 기능 과 함께 이것을 사용 합니다. 그것에 대해 아무것도 해킹되지 않습니다!
Monica와의 가벼움 경주

5

이를 수행하는 파서의 특정 이름은 없습니다. 그러나 나는 이것을 수행하는 하나의 알고리즘을 강조 할 것이다 : 파생물 분석 .

한 번에 하나씩 토큰을 입력합니다. 입력이 끝나면 구문 분석 포리스트가 생성됩니다. 또는 구문 분석 도중 ( 부분 구문 분석)에 전체 구문 분석 포리스트를 가져올 수도 있습니다 .

파생어를 사용한 구문 분석은 컨텍스트없는 문법을 처리하고 모호한 문법에 대한 구문 분석 포리스트를 생성합니다.

그것은 실제로 우아한 이론이지만 초기 단계에 있으며 널리 배포되지는 않습니다. Matt Might에는 Scala / Racket 등의 다양한 구현에 대한 링크 목록이 있습니다.

파생어를 사용하여 인식을 시작하면 (즉, 언어의 파생어를 사용 하는 것으로 시작하고 , 유효한지 여부를 결정하기 위해 일부 입력을 인식하는 것을 목표로), 파생 프로그램으로 구문 분석하도록 프로그램을 변경하면 이론을 더 쉽게 배울 수 있습니다. 즉, 언어의 파생어를 가져 오는 대신 변경하여 파서의 파생어를 가져 와서 구문 분석 포리스트를 계산합니다.


4
Downvoter : downvote의 가치를 설명해 주시겠습니까? 수정하거나 개선해야 할 것이 있으면 알아두면 좋을 것입니다.
Cornstalks

나는 downvoter가 아니며 의견없이 downvoting을 꿈꾸지 않을 것입니다. 그러나 당신의 열정적 인 논문은 복잡성과 파싱 포레스트와 관련하여 동일한 결과를 얻는 많은 기존 파서를 참조하지 않습니다. 함수형 프로그래밍은 훌륭하지만 기존 주제와 결과를 비교하는 것도 좋습니다. 나중에 사용할 수 있도록 구문 분석 포리스트가 얼마나 편리합니까?
babou

@babou : 기록을 위해, 나는 그 블로그 / 종이의 저자가 아닙니다. 그러나 예,이 알고리즘을 다른 알고리즘과 비교하여 더 자세하게 설명하고 자세히 설명 할 수 있음에 동의합니다. Matt Might가 이에 대한 강의를 했지만이 답변에 통합하는 것이 좋을 것입니다. 시간이 있으면이 답변을 확장하려고 노력할 것입니다.
Cornstalks

1
확장에 너무 많은 시간을 소비하지 마십시오. 내가 알 수있는 한, 그것은 OP 이후의 것이 아닙니다. 그의 질문에는주의 깊게 읽어야합니다. 그의 파싱 숲 사용은 당신이 아닙니다. --파생 상품에 관해서는 ... 흥미로운 것처럼 들리지만 이전 작품과 관련이 있어야하며 중요한 내용이 있습니다. 그러나 나는이 대답이 아니라 M Might의 논문이나 그의 블로그를 의미합니다.
babou

2

이상적이지는 않지만 각 입력 줄에서 구문 분석하려고 두 번 이상 수행했습니다. 실패하면 라인을 유지하고 다음 라인을 추가하십시오. 의사 코드에서 :

buffer = ''
for each line from input:
    buffer = buffer + line
    if can parse buffer:
        emit tree
        buffer = ''

큰 문제는 일부 언어에서는 다음 줄을 읽기 전에식이 완전한지 알 수 없다는 것입니다. 이 경우 다음을 읽을 수 있고 유효한 시작인지 또는 연속인지 확인할 수 있습니다 ... 그러나 정확한 언어 구문이 필요합니다.

더 나쁜 것은, 그 언어들에서는 긴 문장이 아니더라도 파일이 끝날 때까지 파싱 할 수없는 병리학 적 사례를 만드는 것은 어렵지 않습니다.


0

간단히 말해서

문제의 가장 빠른 해결책은 가능한 모든 문서의 시작 부분을 인식하는 REGEX 또는 FSA (finite state automaton)를 정의하는 것 같습니다 (거짓 긍정이 허용되며 실제로는 문서에 해당하지 않음). 그런 다음 입력에서 매우 빠르게 실행하여 문서가 거의 오류없이 시작될 수있는 다음 위치를 식별 할 수 있습니다. 문서 시작에 약간의 잘못된 위치가 발생할 수 있지만 파서가이를 인식하여 버립니다.

따라서 유한 상태 오토 마톤은 당신이 찾고 있던 파서 이름이 될 수 있습니다. :)

문제

특히 어휘가 많은 해석을 할 수있는 경우 실제 문제를 이해하는 것은 항상 어렵습니다. parse forest라는 단어는 여러 구문 분석 트리가있는 모호한 문장의 Context-Free (CF) 구문 분석을 위해 만들어졌습니다 (afaik). 문장의 격자를 구문 분석하거나 다른 유형의 문법으로 다소 일반화 할 수 있습니다. 따라서 Earley, GLR, Marpa 및이 경우에는 관련이없는 파생 파서 (많은 다른 것)에 대한 모든 대답이 있습니다.

그러나 그것은 분명히 당신이 생각하는 것이 아닙니다. 문서 의 구문이 정의되는 방식을 실제로 말하지 않기 때문에 모호하지 않은 문서의 시퀀스 인 고유 문자열을 구문 분석하고 각 또는 구조적 표현에 대한 구문 분석 트리를 얻으려고합니다. 공식적인 언어 관점. 문서의 시작 부분에서 시작할 때 구문 분석 작업을 수행하는 알고리즘과 테이블이 있습니다. 그러면 그렇게 해.

실제 문제는 문서 스트림에 문서를 분리하는 상당한 가비지가 포함되어 있다는 것입니다. 그리고 당신의 어려움은이 쓰레기를 충분히 빨리 스캔하는 것 같습니다. 현재 기술은 처음부터 시작하여 첫 번째 문자부터 스캔하고 전체 문서를 스캔 할 때까지 실패 할 때마다 다음 문자에서 다시 시작으로 건너 뛰는 것입니다. 그런 다음 문서가 방금 스캔 된 후 첫 문자부터 반복해서 진술합니다.

그것은 또한 그의 대답 의 두 번째 부분에서 @amon이 제안한 솔루션 입니다.

파서의 코드가 문서의 시작 부분에서 매우 효율적으로 시작되도록 최적화되지 않았기 때문에 매우 빠른 해결책은 아닙니다 (테스트 할 방법이 없습니다). 정상적인 사용에서는이 작업을 한 번만 수행하므로 최적화 관점에서 핫스팟이 아닙니다. 따라서이 솔루션에 대한 당신의 온화한 행복은 그리 놀라운 일이 아닙니다.

따라서 실제로 필요한 것은 대량의 쓰레기로 시작하는 문서의 시작 부분을 빠르게 찾을 수있는 알고리즘입니다. 운 좋게도 : 그러한 알고리즘이 존재합니다. 그리고 나는 당신이 그것을 알고 있다고 확신합니다 : 그것은 REGEX를 찾는 것입니다.

간단한 해결책

해야 할 일은 문서의 사양을 분석하여 이러한 문서가 시작되는 방법을 찾는 것입니다. 구문 사양이 공식적으로 어떻게 구성되어 있는지 잘 모르겠 기 때문에 정확히 어떻게 말할 수는 없습니다. 아마도 그들은 모두 유한 한 목록에서 나온 단어로 시작하고 구두점이나 숫자가 섞여있을 수 있습니다. 그것은 당신이 확인하기위한 것입니다.

FSA (Finite State Automaton)를 정의하거나 대부분의 프로그래머에게 문서의 처음 몇 문자를 인식 할 수있는 정규식 (REGEX)을 정의해야합니다. 큰 (시간과 공간이 걸릴 수 있음). 이는 문서 사양에서 비교적 쉽게 수행 할 수 있어야하며 문서 사양을 읽는 프로그램으로 자동으로 수행 할 수 있습니다.

정규 표현식을 작성한 후에는 입력 스트림에서 실행하여 다음과 같이 첫 번째 (또는 다음) 문서의 시작 부분으로 매우 빠르게 이동할 수 있습니다.

나는 가정 :
- docstart모든 문서의 시작 부분과 일치하는 정규식입니다
- search(regex, stream)함수가되는 검색한다 stream일치하는 문자열에 대한 regex. 리턴하면 스트림은 일치하는 첫 번째 하위 문자열의 시작 부분에서 시작하여 접미어 서브 스트림으로 줄어 듭니다. 또는 빈 스트림으로 일치하는 항목이 없습니다.
- parse(stream)스트림의 시작 부분 (왼쪽에있는 것)에서 문서를 구문 분석하려고 시도하고 구문 분석 트리를 어떤 형식 으로든 리턴하거나 실패합니다. 리턴하면 스트림은 구문 분석 된 문서의 끝 바로 다음 위치에서 시작하여 접미어 서브 스트림으로 줄어 듭니다. 구문 분석에 실패하면 예외를 호출합니다.

forest = empty_forest
search(docstart, stream)
while stream is not empty:
  try:
    forest = forest + parse(stream)
  except
    remove first character from stream
  search(docstart, stream)

첫 번째 문자를 제거해야 다음 검색에서 다시 일치하는 항목을 찾을 수 없습니다.

물론 스트림 단축은 이미지입니다. 스트림의 인덱스 일 수 있습니다.

마지막으로 정규식이 모든 시작을 인식하는 한 너무 정확할 필요는 없습니다. 때때로 문서의 시작이 될 수없는 문자열을 인식하면 (거짓 긍정적) 파서에 대한 쓸모없는 호출 비용은 유일한 페널티입니다.

따라서 가능하다면 정규 표현식을 단순화하는 데 도움이 될 수 있습니다.

더 빠른 솔루션의 가능성에 대해

위의 솔루션은 대부분의 경우에 잘 작동합니다. 그러나 처리 할 가비지와 테라 바이트 단위의 파일이 실제로 많은 경우 더 빠르게 실행되는 다른 알고리즘이있을 수 있습니다.

이 아이디어는 Boyer-Moore 문자열 검색 알고리즘 에서 파생됩니다 . 이 알고리즘은 문자열의 구조적 분석을 사용하여 대부분의 스트림 읽기를 건너 뛰고 조각을 보지 않고도 조각을 뛰어 넘기 때문에 단일 문자열을 매우 빠르게 검색 할 수 있습니다. 단일 문자열에 대해 가장 빠른 검색 알고리즘입니다.

어려운 점은 단일 문자열이 아닌 정규식 검색에 대한 적응이 매우 섬세하게 보이고 고려중인 정규식의 기능에 따라 제대로 작동하지 않을 수 있다는 것입니다. 이는 구문 분석중인 문서의 구문에 따라 달라질 수 있습니다. 그러나 내가 찾은 문서를주의 깊게 읽을 시간이 없었기 때문에 이것을 너무 많이 믿지 마십시오.

나는 분명히 참조 된 연구 논문을 포함하여 웹에서 찾은 하나 또는 두 개의 포인터로 당신 떠날 것 입니다 . 그리고 그것을 할 선반 프로그램이 없을 것입니다.


-2

설명하고있는 것은 SAX와 SOM으로 설명 할 수 있습니다.

SAX-(Simple API for XML)은 XML 문서 용 XML-DEV 메일 링리스트에서 개발 한 이벤트 순차 액세스 구문 분석기 API입니다.

SOM-XML 파일의 메모리 표현에서 (XML Schema Object Model) 랜덤 액세스

C #과 Java에는 두 가지 유형의 구현이 있으며 아마도 더 많을 것입니다. 일반적으로 XSD 또는 DTD는 선택 사항입니다.

SAX의 즐거움은 메모리 오버 헤드가 적기 때문에 큰 XML 파일에 유용합니다. 단점은 SAX를 사용한 임의 액세스가 없거나 느리고 개발 시간이 일반적으로 SOM보다 훨씬 많다는 것입니다. SOM의 명백한 문제는 잠재적으로 큰 RAM 요구 사항입니다.

이 답변이 모든 플랫폼 및 모든 언어에 적용되는 것은 아닙니다.


1
OP가 XML을 구문 분석하고 있다고 생각하는 이유는 무엇입니까?
Dan Pichelman

1
이것은 질문에 대답하지 않습니다.

@Snowman 수락 된 답변의 전반부를 포함하여 지금까지 거의 아무것도 대답하지 못했습니다. 다른 사람을 고르는 데 아무런 의미가 없습니다. 질문은주의 깊게 읽어야합니다.
babou

@babou 나는 누구를 선택하지 않았고, 나의 downvote를 설명하고있었습니다.

@Snowman은 내 downvote를 설명 합니다. 그것은 공정하며 더 많은 사용자가 그렇게하기를 바랍니다. 나는 원어민이 아니에요. 그를 뽑는 것이 너무 강할 수도 있습니다. 모두가 부주의 한 가정을하고 있다는 것입니다. 따라서 주목할 가치가 없습니다. 이 것이 다른 것보다 조금 더 비싸다는 것은 사실입니다.
babou
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.