Разбор своеобразного унарного знака минус с помощью Spirit.Lex

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

 -x       // unary
 x - y    // binary
 x-y      // binary
 x -y     // unary
 x- y     // binary
 (- y ... // unary

Примечание. Открытое имя в последнем правиле можно заменить любым токеном в языке, кроме "идентификатор", "число" и "близкий_парень".

Примечание. В четвертом случае х является идентификатором. Идентификатор может представлять собой собственное утверждение. И -у, это отдельное утверждение.

Поскольку тип знака минуса зависит от пробелов, я подумал, что мне нужно будет вернуть два разных токена от лексера, один для унарного минуса и один для двоичного минуса. Любые идеи, как я могу это сделать?

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

#ifndef LEXER_H
#define LEXER_H

#include <iostream>
#include <algorithm>
#include <string>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_algorithm.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>

#define BOOST_SPIRIT_LEXERTL_DEBUG 1

using std::string;
using std::cerr;

namespace skill {

   namespace lex = boost::spirit::lex;
   namespace phoenix = boost::phoenix;

   // base iterator type
   typedef string::iterator BaseIteratorT;

   // token type
   typedef lex::lexertl::token<BaseIteratorT, boost::mpl::vector<int, string> > TokenT;

   // lexer type
   typedef lex::lexertl::actor_lexer<TokenT> LexerT;

   template <typename LexerT>
   struct Tokens: public lex::lexer<LexerT>
   {
      Tokens(const string& input):
         lineNo_(1)
      {
         using lex::_start;
         using lex::_end;
         using lex::_pass;
         using lex::_state;
         using lex::_tokenid;
         using lex::_val;
         using lex::omit;
         using lex::pass_flags;
         using lex::token_def;
         using phoenix::ref;
         using phoenix::count;
         using phoenix::construct;

         // macros
         this->self.add_pattern
            ("EXP",     "(e|E)(\\+|-)?\\d+")
            ("SUFFIX",  "[yzafpnumkKMGTPEZY]")
            ("INTEGER", "-?\\d+")
            ("FLOAT",   "-?(((\\d+)|(\\d*\\.\\d+)|(\\d+\\.\\d*))({EXP}|{SUFFIX})?)")
            ("SYMBOL",  "[a-zA-Z_?@](\\w|\\?|@)*")
            ("STRING",  "\\\"([^\\\"]|\\\\\\\")*\\\"");

         // whitespaces and comments
         whitespaces_ = "\\s+";
         comments_    = "(;[^\\n]*\\n)|(\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/)";

         // literals
         float_   = "{FLOAT}";
         integer_ = "{INTEGER}";
         string_  = "{STRING}";
         symbol_  = "{SYMBOL}";

         // operators
         plus_          = '+';
         difference_    = '-';
         minus_         = "-({SYMBOL}|\\()";

         // ... more operators

         // whitespace
         this->self += whitespaces_
            [
               ref(lineNo_) += count(construct<string>(_start, _end), '\n'),
               _pass = pass_flags::pass_ignore
            ];

         // a minus between two identifiers, numbers or close-open parens is a binary minus, so add spaces around it
         this->self += token_def<omit>("[)a-zA-Z?_0-9]-[(a-zA-Z?_0-9]")
            [
               unput(_start, _end, *_start + construct<string>(" ") + *(_start + 1) + " " + *(_start + 2)),
               _pass = pass_flags::pass_ignore
            ];

         // operators (except for close-brackets) cannot be followed by a binary minus
         this->self += token_def<omit>("['`.+*<>/!~&|({\\[=,:@](\\s+-\\s*|\\s*-\\s+)")
            [
               unput(_start, _end, *_start + construct<string>("-")),
               _pass = pass_flags::pass_ignore
            ];

         // a minus directly preceding a symbol or an open paren is a unary minus
         this->self += minus_
            [
               unput(_start, _end, construct<string>(_start + 1, _end)),
               _val = construct<string>("-")
            ];

         // literal rules
         this->self += float_ | integer_ | string_ | symbol_;

         // ... other rules
      }

      ~Tokens() {}

      size_t lineNo() { return lineNo_; }

      // ignored tokens
      token_def<omit> whitespaces_, comments_;

      // literal tokens
      token_def<int> integer_;
      token_def<string>  float_, symbol_, string_;

      // operator tokens
      token_def<> plus_, difference_, minus_; // minus_ is a unary minus
      // ... other tokens

      // current line number
      size_t lineNo_;
   };
}

#endif // LEXER_H

По сути, я определил двоичный минус (называется difference в коде) быть любым знаком минус, который имеет пробелы с обеих сторон и использует unput для обеспечения этого правила. Я также определил унарный минус как знак минус, который непосредственно предшествует символу или открытому парену, и снова использовал unput для обеспечения соблюдения этого правила (для чисел знак минус является частью токена).

0 ответов

Другие вопросы по тегам