2016-10-22 8 views
1

제목이 불명확하면 미안하고 렉싱 및 구문 분석을 처음 사용합니다.Jison 렉서를 사용하여 여러 토큰을 반환하는 방법

기본적으로 나는 일부 텍스트를 구문 분석하기 위해 Jison을 사용하고 있으며 렉서가 들여 쓰기를 이해하도록 노력하고 있습니다. 질문에 조금 있습니다 :

(\r\n|\r|\n)+\s*  %{ 
         parser.indentCount = parser.indentCount || [0]; 

         var indentation = yytext.replace(/^(\r\n|\r|\n)+/, '').length; 

         if (indentation > parser.indentCount[0]) { 
          parser.indentCount.unshift(indentation); 
          return 'INDENT'; 
         } 

         var tokens = []; 

         while (indentation < parser.indentCount[0]) { 
          tokens.push('DEDENT'); 
          parser.indentCount.shift(); 
         } 

         if (tokens.length) { 
          return tokens; 
         } 

         if (!indentation.length) { 
          return 'NEWLINE'; 
         } 
         %} 

지금까지 거의 모든 것이 예상대로 작동합니다. 한 가지 문제는 DEDENT 토큰 배열을 반환하려고 시도하는 선입니다. Jison이 그 배열을 Expecting ........, got DEDENT,DEDENT과 같은 구문 분석 오류를 발생시키는 문자열로 변환하는 것 같습니다.

이 문제를 해결하기 위해 내가 할 수있는 일은 스택에 DEDENT 토큰을 수동으로 밀어 넣는 것입니다. 어쩌면 this.pushToken('DEDENT') 또는 그 라인을 따라 뭔가 같은 기능. 그러나 Jison 문서는별로 좋지 않아 도움을받을 수 있습니다.

의견이 있으십니까?

편집 :

내가 보인다는 생성 된 파서 코드보고 후이 문제를 내 방식을 해킹 할 수 있었다합니다. 여기

if (tokens.length) { 
    var args = arguments; 

    tokens.slice(1).forEach(function() { 
    lexer.performAction.apply(this, args); 
    }.bind(this)); 

    return 'DEDENT'; 
} 

이 트릭 렉서 따라서 적절한 dedents에 추가 할 수 있도록, 우리는 스택에있는 각 DEDENT에 대한 정확한 동일한 입력을 사용하여 다른 작업을 수행하기에 ... 작동하는 것 같다거야. 그러나, 그것은 심한 느낌과 예기치 않은 문제가있을 수 있습니다 걱정입니다.

누구든지 더 좋은 방법으로이 작업을 수행 할 수 있다면 나는 그것을 좋아할 것입니다.

답변

1

며칠 후에 나는 더 나은 대답을 알아 냈습니다.

(\r\n|\r|\n)+[ \t]* %{ 
         parser.indentCount = parser.indentCount || [0]; 
         parser.forceDedent = parser.forceDedent || 0; 

         if (parser.forceDedent) { 
          parser.forceDedent -= 1; 
          this.unput(yytext); 
          return 'DEDENT'; 
         } 

         var indentation = yytext.replace(/^(\r\n|\r|\n)+/, '').length; 

         if (indentation > parser.indentCount[0]) { 
          parser.indentCount.unshift(indentation); 
          return 'INDENT'; 
         } 

         var dedents = []; 

         while (indentation < parser.indentCount[0]) { 
          dedents.push('DEDENT'); 
          parser.indentCount.shift(); 
         } 

         if (dedents.length) { 
          parser.forceDedent = dedents.length - 1; 
          this.unput(yytext); 
          return 'DEDENT'; 
         } 

         return `NEWLINE`; 
         %} 

는 첫째, 내가 실수로 비 개행 공간의 일련의 후 별도의 줄 바꿈을 포착되지 않은 확인하기 위해 내 캡처 정규식을 수정 : 다음과 같이 표시됩니다.

다음으로 "글로벌"변수가 2 개 있는지 확인합니다. indentCount은 현재 들여 쓰기 길이를 추적합니다. forceDedent은 0보다 큰 값이 있으면 DEDENT을 반환하도록합니다.

다음으로 진리 값을 forceDedent에서 테스트 할 조건이 있습니다. 우리가 가지고 있다면, 우리는 1 씩 감소시키고 unput 함수를 사용하여 같은 패턴을 적어도 한 번 더 반복하도록합니다. 그러나이 반복에서는 DEDENT을 반환 할 것입니다.

반환하지 않은 경우 현재 들여 쓰기 길이가 표시됩니다.

현재 들여 쓰기가 가장 최근의 들여 쓰기보다 큰 경우 우리는 indentCount 변수에서이를 추적하여 INDENT을 반환합니다.

반환하지 않은 경우 가능한 공석을 준비해야합니다. 우리는 그들을 추적하기 위해 배열을 만들 것입니다.

헌납이 감지되면 사용자는 한 번에 하나 이상의 블록을 닫으려고 시도 할 수 있습니다. 따라서 사용자가 닫는 블록 수만큼 DEDENT을 포함해야합니다.루프를 설정하고 현재 들여 쓰기가 가장 최근의 들여 쓰기보다 작 으면 DEDENT을 목록에 추가하고 항목을 indentCount에서 벗어나게 할 것입니다.

우리가 헌정을 추적했다면, 모든 헌정이 렉서에 의해 반환되도록해야합니다. 렉서는 한 번에 하나의 토큰 만 반환 할 수 있기 때문에 여기서 1을 반환 할 것이지만 우리는 또한 forceDedent 변수를 설정하여 나머지를 반환하도록합니다. 이 패턴을 다시 반복하고 공헌을 삽입 할 수 있도록 unput 함수를 사용합니다.

다른 경우에는 NEWLINE 만 반환합니다.