2016-10-22 2 views
1

Я новичок в лексинге и разоблачении, так что извините, если заголовок недостаточно ясен.Как вернуть несколько токенов с помощью Jison lexer

В принципе, я использую 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 не так велика, и я мог бы использовать некоторую помощь.

Любые мысли?

EDIT:

Я, кажется, удалось взломать мой путь вокруг этого, посмотрев на сгенерированный код парсера. Вот что, кажется, работает ...

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

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

    return 'DEDENT'; 
} 

Это фокусы лексера в выполнении другого действия, используя один и тот же вход для каждого DEDENT мы имеем в стеке, таким образом, позволяя добавлять в соответствующем dedents. Тем не менее, это кажется грубым, и я беспокоюсь, что могут возникнуть непредвиденные проблемы.

Мне все равно понравилось бы, если бы у кого-нибудь были идеи по лучшему способу сделать это.

ответ

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 заставит нас вернуть DEDENT, если оно имеет значение выше 0.

Далее у нас есть условие, чтобы проверить истинное значение на forceDedent. Если он у нас есть, мы уменьшим его на 1 и воспользуемся функцией unput, чтобы убедиться, что мы повторяем этот же шаблон по крайней мере еще раз, но для этой итерации мы вернем DEDENT.

Если мы не вернулись, мы получим длину нашего текущего отступа.

Если текущий отступ больше нашего последнего отступа, мы будем отслеживать это на нашей переменной indentCount и возвращать INDENT.

Если мы не вернулись, пришло время подготовиться к возможным отступникам. Мы сделаем массив, чтобы отслеживать их.

Когда мы обнаруживаем разделитель, пользователь может попытаться закрыть один или несколько блоков одновременно. Поэтому нам нужно включить DEDENT столько блоков, сколько пользователь закрывает.Мы установили цикл и сказали, что пока текущий отступ меньше нашего последнего отступа, мы добавим DEDENT в наш список и сдвинем элемент с нашего indentCount.

Если мы отслеживаем любые разделители, нам нужно убедиться, что все они будут возвращены лексером. Поскольку лексер может возвращать только один токен за один раз, мы вернем здесь 1, но мы также установим переменную forceDedent, чтобы убедиться, что мы также оставим остальные. Чтобы убедиться, что мы повторяем этот шаблон снова, и эти разделители могут быть вставлены, мы будем использовать функцию unput.

В любом другом случае мы просто вернем NEWLINE.

Смежные вопросы