2015-01-10 1 views
1

숫자와 수학 표현식 만 포함하는 매우 단순한 언어에 대한 매우 간단한 구문 분석기를 만들려고합니다. 궁극적으로이 확장을 계획하지만이 기본 버전을 작동시킬 때까지는 아닙니다.반복적 인 Spirit.Qi 문법으로 인한 세그먼트 오류

가 나는 성공적으로 분석했습니다

1 
425 
1 + 1 
1 - 1 
1 * 1 
1/1 

없음 문제. 하지만 다음과 같이 입력을 구문 분석하여 재귀 적으로 만들고 싶었습니다.

1 + 2 - 3 

세그먼트 화 오류가 발생하기 시작했습니다. 재귀 문법과 세분화 오류로 인해 인터넷 검색을 한 적이 있습니다. 발견 한 내용을이 문법에 적용하여 작동시키지 못하는 것 같습니다. 이것은 제 상황에 어울리지 못하거나 제 qi 문법에 어떤 일이 일어나고 있는지 정확하게 이해하지 못하기 때문입니다.

내 문법 (융합 적응 포함) 다음과 같은 구조체로 구성

namespace fun_lang { 
    namespace qi = boost::spirit::qi; 
    namespace ascii = boost::spirit::ascii; 
    namespace phoenix = boost::phoenix; 
    namespace fusion = boost::fusion; 

    struct number_node { 
     long value; 
    }; 

    struct operation_node; 

    typedef boost::variant< 
     boost::recursive_wrapper<operation_node>, 
     number_node 
    > node; 

    struct operation_node { 
     node left, right; 
     char op; 
    }; 

    struct program { 
     std::vector<node> nodes; 
    }; 
} 

BOOST_FUSION_ADAPT_STRUCT(fun_lang::program, (std::vector<fun_lang::node>, nodes)); 
BOOST_FUSION_ADAPT_STRUCT(fun_lang::number_node, (long, value)); 
BOOST_FUSION_ADAPT_STRUCT(fun_lang::operation_node, (fun_lang::node, left) (char, op) (fun_lang::node, right)); 

namespace fun_lang { 
    template <typename Iterator, typename Skipper> 
    struct fun_grammar : qi::grammar<Iterator, program(), Skipper> { 
     fun_grammar() : fun_grammar::base_type(start) { 
      using ascii::char_; 
      using qi::ulong_; 
      using qi::_val; 
      using qi::_1; 

      using phoenix::push_back; 
      using phoenix::at_c; 

      expression = (integer | operation)[_val = _1]; 

      oper = (char_('+') | char_('-') | char_('*') | char_('/'))[_val = _1]; 
      integer = ulong_[at_c<0>(_val) = _1]; 

      operation = expression[at_c<0>(_val) = _1] >> oper[at_c<1>(_val) = _1] >> expression[at_c<2>(_val) = _1]; 

      start = *expression[push_back(at_c<0>(_val), _1)]; 
     } 

     qi::rule<Iterator, program(), Skipper> start; 
     qi::rule<Iterator, number_node(), Skipper> integer; 
     qi::rule<Iterator, char(), Skipper> oper; 
     qi::rule<Iterator, node(), Skipper> expression; 
     qi::rule<Iterator, operation_node(), Skipper> operation; 
    }; 
} 

규칙 구조의 일부는 내가하는 방법에 대한 참조로 사용 된 다른 언어로 쓴 yacc에 문법을 기반으로 이러한 규칙을 구조화합니다. 세분화 오류를 일으키는 것이 무엇인지 모르겠지만이를 실행할 때 나는 그것이 무엇인지 알 수 있습니다. 규칙 단순화, 중간 규칙 제거 및 비 재귀 메서드 테스트를 시도했습니다. 재귀가 아닌 것은 작동하는 것처럼 보이지만 재귀 규칙을 사용하여 Spirit의 많은 예를 보았 기 때문에 성공한 규칙을 표현하는 방법을 제대로 이해하지 못했다고 생각합니다.

편집이 ideone에 대부분의 정확한 복사본을 찾을 수있는 문제 해결에 도움을 들어

. 이데온 버전과 내가 로컬에 가지고있는 유일한 차이점은 표준 입력에서 직접 가져 오는 파일을 읽는 대신에 가능하다는 것입니다.

답변

3

스택 오버플로의 두 가지 소스 (세분화 오류로 끝남)가 있습니다. 하나는 operation_nodenode의 생성자입니다. boost::variant은 기본 생성시 기본 템플릿 생성 객체로 초기화됩니다. 이것은 boost::recursive_wrapper<operation_node>이며 을 구성하는 두 개의 node을 구성하는 operation_node을 구성하며 스택이 고갈 될 때까지 계속됩니다.

정신에 변형을주는 것이 일반적이다 그래서

struct nil { }; 

typedef boost::variant< 
    nil, 
    boost::recursive_wrapper<operation_node>, 
    number_node 
> node; 

이 해결됩니다,이를 방지하고 초기화되지 않은 변형을 식별 할 수있는 방법을 가지고 첫 번째 인수로 struct nil { }; 같은 무기 호 유형을 문법. 당신이 nil 유형을 사용하지 않으려면 number_node가 문제없이 구성 할 수 있기 때문에

typedef boost::variant< 
    number_node, 
    boost::recursive_wrapper<operation_node> 
> node; 

또한 경우에 작동합니다.

다른 스택 오버플로는 Boost.Spirit이 LL (inf) 파서를 생성하기 때문에 (Yacc은 LALR (1) 파서를 생성 함) 반 누름 파서가 생성된다는 것을 의미합니다.규칙

expression = (integer | operation)[_val = _1]; 
operation = expression[at_c<0>(_val) = _1] >> oper[at_c<1>(_val) = _1] >> expression[at_c<2>(_val) = _1]; 

는 입력을 사용하지 않고 expression 다시 operation에로 operation에서 내려 파서를 생성합니다. 스택 오버플로가 발생할 때까지이 작업이 반복됩니다. 그러면 다른 세그 폴트가 발생합니다.

이 문제가 사라질 operation

operation = integer[at_c<0>(_val) = _1] >> oper[at_c<1>(_val) = _1] >> expression[at_c<2>(_val) = _1]; 

대로 규칙을 재구성합니다. 또한, 당신은 operation이 찾을 수있는 기회를 가지고 전에 그렇지 않으면 integer 부분이 성공적으로 일치합니다, 당신이 기대하는 생각 경기가 작동하려면

expression = (operation | integer)[_val = _1]; 

expression 규칙을 다시 작성해야하고, 파서는 것이다 부분 일치가 성공적이기 때문에 역 추적하지 않습니다.

성령 파서는 기인합니다. 사용하는 구문 분석기 작업은 대개 불필요합니다. 다음과 같이 문법을 대량으로 다시 작성할 수 있습니다.

expression = operation | integer; 

oper = char_("-+*/"); 
integer = ulong_; 

operation = integer >> oper >> expression; 
+0

흠. LL 파서가 복잡한 언어를 정의 할 때 약한 것처럼 보입니다. 그러나 스피릿에 대한 (매우 기본적인) 경험으로 복잡한 구문보다는 간단한 구문 분석 문제를 해결할 수 있다고 생각합니다. –

+2

나는 그것을 말하지 않을 것이다. LALR 파서는 LL 파서 (그리고 LR 파서가 여전히 강력 함)보다 강력하고 효율적인 반면, Boost.Spirit은 매우 강력합니다. 무한한 선견자와 모든 것. 야생에서 발견 할 수있는 대부분의 언어는 LL 파서로 파싱 할 수 있으며 LL (inf)로도 편리하게 사용할 수 있습니다. 왼쪽 재귀 문법을 공식화하지 않는 것이 LALR로 처리 할 필요가없는 LL 파서로 처리해야하는 유일한 중요한 것입니다. – Wintermute

+0

둘을 구분하는 데 더 많은 시간을 할애해야합니다. 나는 파싱과 문법 그리고 모든 좋은 것들에 관해서는 상당히 초록색이다. 위대한 답변을 주셔서 감사합니다, 그것은 입력을 위해 일했습니다. 지금은 (1 + 2) + 10을 위해 어디로 가야할 지 모르겠지만 좀 더 읽고 마음껏 시도 할 것입니다. –