zoukankan      html  css  js  c++  java
  • 經典表達式遞歸下將法語法分析和求值 yongmou

    首先,給出文法定義:

    經典表達式文法:
        Expr   ->  Expr + Term
                      |Expr - Term
                      |Term
        Term   ->  Term * Factor
                       |Term / Factor
                       |Factor
        Factor ->  (Expr)
                       |num
    遞歸下降法,無法處理左遞歸文法

    消除左遞歸後,用BNF表示
        <Expr>    ->  <Term> { (+|-) <Term> }
        <Term>   ->  <Factor> { (*|/) <Factor> }
        <Factor>  ->  (<Expr>) | num | indent

    程序:

    1 /*
    2 遞歸下降法構造經典表達式的語法分析
    3 編寫遞歸下降子程序有一個協定:
    4 每一個子程序都在全局量nextToken中放入下一個輸入的token。
    5 因而,當開始一個語法分析函數時,假定nextToken具有還沒有被語法分析過程使用的輸入標記中最左面的那個標記。
    6  */
    7 #include <stdio.h>
    8 #include <ctype.h>
    9 #include <stdlib.h>
    10
    11  enum token_type{
    12 NUMBER, OPERATOR, SEPARATOR, UNKOWN
    13 };
    14
    15  struct Token{
    16 int type;
    17 int val;
    18 };
    19
    20  struct Token next_token; // 下一個未處理的token
    21 // put next token into next_token
    22 void lex();
    23
    24 // 三個非終結符
    25 double expr();
    26 double term();
    27 double factor();
    28
    29 int main()
    30 {
    31 // input:
    32 // 每個表達式, 以';'結尾
    33 freopen("test.in", "r", stdin);
    34
    35 while (1){
    36 lex(); // 取得第一個token
    37 if (next_token.type == UNKOWN && next_token.val == EOF)
    38 break;
    39
    40 printf("%lf\n", expr());
    41
    42 if (next_token.type != SEPARATOR || next_token.val != ';' ){
    43 fprintf(stderr, "未以\';\'結尾\n");
    44 break;
    45 }
    46 }
    47 return 0;
    48 }
    49
    50 // <expr> -> <term> { (+ | -) <term> }
    51 double expr()
    52 {
    53 double val;
    54
    55 // parse first term
    56 val = term();
    57
    58 // As long as the next token is + or - ,
    59 // call lex() get next token,
    60 // and parse the next term.
    61 while(next_token.type == OPERATOR &&
    62 (next_token.val == '+' || next_token.val == '-')){
    63 int op = next_token.val;
    64 lex();
    65
    66 if(op == '+')
    67 val += term();
    68 else if(op == '-')
    69 val -= term();
    70 }
    71 return val;
    72 }
    73
    74 // <term> -> <factor> { (* | /) <factor> }
    75 double term()
    76 {
    77 double val;
    78
    79 // parse first factor
    80 val = factor();
    81
    82 // As long as the next token is * or / ,
    83 // call lex() get next token,
    84 // and parse the next factor.
    85 while(next_token.type == OPERATOR &&
    86 (next_token.val == '*' || next_token.val == '/')){
    87 int op = next_token.val;
    88 lex();
    89
    90 if(op == '*')
    91 val *= factor();
    92 else if(op == '-')
    93 val /= factor();
    94 }
    95 return val;
    96 }
    97
    98 // <factor> -> (<expr>) | num
    99 double factor(){
    100 double val;
    101 if(next_token.type == SEPARATOR && next_token.val == '('){
    102 lex();
    103 val = expr();
    104 if(next_token.type == SEPARATOR && next_token.val == ')'){
    105 lex(); // 讀取下一個token
    106 }else{
    107 fprintf(stderr, "括號不匹配\n");
    108 exit(1);
    109 }
    110 }else if(next_token.type == NUMBER){
    111 val = next_token.val;
    112 lex();
    113 }else{ // 既不是數字也不是'('
    114 fprintf(stderr, "unkown factor: %c\n", next_token.val);
    115 exit(1);
    116 }
    117 return val;
    118 }
    119
    120 void lex(){
    121 int c;
    122 while((c = getchar()) != EOF && isspace(c))
    123 ;
    124 if(isdigit(c)){
    125 int val;
    126 val = c - '0';
    127 while(isdigit(c = getchar()))
    128 val = val * 10 + c - '0';
    129 ungetc(c, stdin);
    130 next_token.type = NUMBER;
    131 next_token.val = val;
    132 return;
    133 }
    134 next_token.val = c;
    135 switch(c){
    136 case '+':
    137 case '-':
    138 case '*':
    139 case '/':
    140 next_token.type = OPERATOR;
    141 break;
    142 case '(':
    143 case ')':
    144 case ';':
    145 next_token.type = SEPARATOR;
    146 break;
    147 default:
    148 next_token.type = UNKOWN;
    149 }
    150 }

    測試數據, 以分號結尾的表達式,main()中將stdin重定向到文件test.in

    12 + 2*30;
    (1+2)* 5;
    4+5;
    8+8*7;
    
  • 相关阅读:
    Eclipse的常见使用错误及编译错误
    Android学习笔记之Bundle
    Android牟利之道(二)广告平台的介绍
    Perl dbmopen()函数
    Perl子例程(函数)
    Perl内置操作符
    Perl正则表达式
    Linux之间配置SSH互信(SSH免密码登录)
    思科路由器NAT配置详解(转)
    Windows下查看端口被程序占用的方法
  • 原文地址:https://www.cnblogs.com/liyongmou/p/2010139.html
Copyright © 2011-2022 走看看