Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Синтаксический анализатор


Автор: AlanG 8.7.2006, 17:11
Всем доброго времени суток.
Последние пару дней, без преувеличения, разбирал синтаксический анализатор, в целом логика кода понятна, но имеется ньюанс. Сам код что ниже особо не читайте, он большой и запутанный, сам не мало литров пива выкушал smile , смотрите еще ниже. В книге полного описания и разбора кода нет к сожелению.
А непонятно следующее: Когда происходит анализ типа лексемы (Number, переменная или оператор), то в двух случаях, когда значение по адресу на который ссылается exp_ptr является Number или переменная, происходит вызов функции isdelim() почемуто smile  проверяет значение на +-*/%^=()  или c==9 или  c=='\r' или c==0, т.е. когда происходит первый вызов, функция без базара возвратит ноль, что повлечет за собой занесение значение в исходный массив и инкрементирование адреса. Далее, что интересно, проверяется следующий символ smile Ну к примеру он есть знак +, получается что функция возвратит 1 и цикл while прекратится, при этом этот + ни куда не заносится, мало того за место него потом будет знак \0 когда функция подойдет к концу.

Блин кучу лишней инфы наверное привел, если что извините, просто уже второй день парюсь smile  
Может логика у меня кривая, может что не так делаю, подскажите плиз smile 
Вот часть кода.
Код

void parser::get_token(){

 register char *temp;

 tok_type = 0;
 temp=token; //
 *temp='\0';

 if(!*exp_ptr) return;

 while(isspace(*exp_ptr)) ++exp_ptr;

 if(strchr("+-*/%^=()", *exp_ptr)){
   tok_type=DELIMITER;
   //Ia?aoia ia neaao?uee neiaie
   *temp++=*exp_ptr++;
  }
  else if(isalpha(*exp_ptr)){
   while(!isdelim(*exp_ptr)) *temp++=*exp_ptr++;
   tok_type=VARIABLE;
 }
  else if(isdigit(*exp_ptr)){
   while(!isdelim(*exp_ptr)) *temp++=*exp_ptr++;
   tok_type=NUMBER;
  }
 *temp='\0';

}
 int parser:: isdelim(char c){

  if(strchr(" +-*/%^=()", c) || c==9 || c=='\r' ||c==0)
   return 1;
  return 0; //

}


Полный код:
Код

#include<iostream>
#include<cstdlib>
#include<cctype>
#include<cstring>
using namespace std;

enum types {DELIMITER =1, VARIABLE, NUMBER};

class parser {
 char tok_type;
 char token[80];
 char* exp_ptr;

 void eval_exp2(double &result);
 void eval_exp3(double &result);
 void eval_exp4(double &result);
 void eval_exp5(double &result);
 void eval_exp6(double &result);
 void atom(double &result);
 void get_token();
 void serror(int error);
 int isdelim(char c);

public: parser();
double eval_exp(char *exp);

};

parser:: parser()
{
  exp_ptr=NULL;
};

double parser:: eval_exp(char *exp)
{
 double result;
 exp_ptr=exp;
 get_token();
 if(!*token)
 {
  serror(2);
  return 0.0;
 }
 eval_exp2(result);
 if(*token) serror(0);
 return result;
}
void parser:: eval_exp2(double &result){
  register char op;
  double temp;
 eval_exp3(result);
 while((op=*token)=='+' || op=='-') {
  get_token();
  eval_exp3(temp);
  switch(op){
   case '-':
     result=result-temp;
     break;
   case '+':
     result=result+temp;
     break;

  }
 }
}
 void parser:: eval_exp3(double &result){
  register char op;
  double temp;

 eval_exp4(result);
 while((op=*token)=='*' || op=='/' || op =='%') {
    get_token();
    eval_exp4(temp);
    switch(op){
     case '*':
      result = result*temp;
      break;
     case '/':
      result=result/temp;
      break;
     case '%':
      result=(int)result % (int)temp;
      break;
    }
  }
 }
void parser:: eval_exp4(double &result)
{
  double temp, ex;
  register int t;

 eval_exp5(result);
  if(*token=='^'){
  get_token();
  eval_exp4(temp);
  ex=result;
  if(temp==0.0) {
   result=1.0;
   return;

   }
  for(t=(int)temp-1; t>0; --t) result = result*(double)ex;
 }
}
 void parser:: eval_exp5(double &result)
{
  register char op;
  op=0;
  if((tok_type == DELIMITER) && *token=='+' || *token == '-') {
  op = *token;
  get_token();
 }
 eval_exp6(result);
 if(op=='-') result=-result;
}
  void parser:: eval_exp6(double &result)
{
  if(*token =='(') {
  get_token();
  eval_exp2(result);
   if(*token!=')')
   serror(1);
  get_token();
  }
  else atom(result);
}
 void parser:: atom(double &result)
{
  switch(tok_type){
  case NUMBER:
   result=atof(token);
   get_token();
   return;
  default:
   serror(0);
 }
}
 void parser:: serror(int error){

    static char *e[]={
    "Sintaxicheskii Oshibka",
    "Balance scoboc",
    "Pusto"
  };
  cout << e[error] << endl;
 }

 void parser::get_token(){

 register char *temp;

 tok_type = 0;
 temp=token; //
 *temp='\0';

 if(!*exp_ptr) return;

 while(isspace(*exp_ptr)) ++exp_ptr;

 if(strchr("+-*/%^=()", *exp_ptr)){
   tok_type=DELIMITER;
   //Ia?aoia ia neaao?uee neiaie
   *temp++=*exp_ptr++;
  }
  else if(isalpha(*exp_ptr)){
   while(!isdelim(*exp_ptr)) *temp++=*exp_ptr++;
   tok_type=VARIABLE;
 }
  else if(isdigit(*exp_ptr)){
   while(!isdelim(*exp_ptr)) *temp++=*exp_ptr++;
   tok_type=NUMBER;
  }
 *temp='\0';

}
 int parser:: isdelim(char c){

  if(strchr(" +-*/%^=()", c) || c==9 || c=='\r' ||c==0)
   return 1;
  return 0; //

}

int main()
{
  char expstr[80];

  cout<<"Aey ieii?aiey aaaaeoa oi?eo, \n";

  parser ob;

  for(;;) {
   cout<<"Aaaaeoa au?a?aiea:";
   cin.getline(expstr, 79);
   if(*expstr=='.') break;
   cout<<"Ioaao:" << ob.eval_exp(expstr)<<"\n\n";
 };
 return 0;
}
  

Автор: slava72 10.7.2006, 10:12
Дык все прозрачно  smile Функция isdelim - являтся (крайне коряво сделанной) функциий проверки символа, на "разделитель".  Т.е. встретив число алфавитночисловой символ - мы считываем строку до первого разделителя, не включая его в лексемму.

А корявость функции - например '\b' и '\n' - разделителями не являются ! smile 
Как правило такие функции пишутся на основе массива флагов, что -то типа:

Код


class parser {
...
public:
  enum {IS_DELIM = 1, IS_SPACE = 2, IS_ALPHA = 4, IS_OP = 8, ... }; // определяем флаги
  void InitFlags(); // Либо в конструкторе
  ...
  bool IsDelim(unsigned char c) { return is_flags_[c] & IS_DELIM; }
protected:
  unsigned is_flags_[256]; // массив флагов
}



Если нужно несколько экземпляров анализатора, то InitFlags() и is_flags можно сделать статическими.

 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)