算术表达式解析(第三版)词法分析版

  以前写过两版算术表达式解析代码,但都是基于栈结构或者树模型的,并不是通用的算法。其实算术表达式解析是最基本的词法分析算法,直到我看了《自制编程语言》里面介绍的递归向下分析法,才明白这种问题的终极解决方案是使用词法分析和语法分析。。

//用于词法定界的头文件 token.h
1
#ifndef _TOKEN_H_VERSION_20140930 2 #define _TOKEN_H_VERSION_20140930 3 4 enum TokenKind{ 5 BAD_TOKEN, 6 NUMBER_TOKEN, 7 ADD_OPERATOR_TOKEN, 8 SUB_OPERATOR_TOKEN, 9 MUL_OPERATOR_TOKEN, 10 DIV_OPERATOR_TOKEN, 11 LEFT_PAREN_TOKEN, 12 RIGHT_PAREN_TOKEN, 13 END_OF_LINE_TOKEN, 14 }; 15 16 #define MAX_TOKEN_SIZE (100) 17 18 struct Token{ 19 TokenKind kind; 20 double value; 21 char str[MAX_TOKEN_SIZE]; 22 }; 23 24 void set_line(char* line); 25 void get_token(Token* token); 26 27 #endif

 

 //词法分析器或者叫单词收割机 lexianalizer.cpp
1
#include "stdafx.h" 2 #include "token.h" 3 #include "stdio.h" 4 #include "stdlib.h" 5 #include "ctype.h" 6 7 static char* st_line; 8 static int st_line_pos; 9 10 typedef enum{ 11 INITIAL_STATUS, 12 IN_INT_PART_STATUS, 13 DOT_STATUS, 14 IN_FRAC_PART_STATUS, 15 }LexerStatus; 16 17 //只是为了获取一个token,里面的循环是忽略空格和读取数字用的 18 void get_token(Token* token) 19 { 20 int out_pos = 0; 21 LexerStatus status = INITIAL_STATUS; 22 char current_char; 23 24 token->kind = BAD_TOKEN; 25 while(st_line[st_line_pos] != '\0') 26 { 27 current_char = st_line[st_line_pos]; 28 //读取数字 29 if((status == IN_INT_PART_STATUS || status == IN_FRAC_PART_STATUS) 30 && !isdigit(current_char) && current_char != '.') 31 { 32 token->kind = NUMBER_TOKEN; 33 sscanf_s(token->str, "%lf", &token->value); 34 return; 35 } 36 //忽略空格 37 if(isspace(current_char)) 38 { 39 if(current_char == '\n') 40 { 41 token->kind = END_OF_LINE_TOKEN; 42 return; 43 } 44 st_line_pos++; 45 continue; 46 } 47 //token太长错误 48 if(out_pos >= MAX_TOKEN_SIZE-1){ 49 fprintf(stderr, "token too long\n"); 50 exit(1); 51 } 52 //token收下这个char 53 token->str[out_pos] = st_line[st_line_pos]; 54 st_line_pos++; 55 out_pos++; 56 token->str[out_pos] = '\0'; 57 58 //根据这个char判断token的kind 59 if(current_char == '+'){ 60 token->kind = ADD_OPERATOR_TOKEN; 61 return; 62 } 63 if(current_char == '-'){ 64 token->kind = SUB_OPERATOR_TOKEN; 65 return; 66 } 67 if(current_char == '*'){ 68 token->kind = MUL_OPERATOR_TOKEN; 69 return; 70 } 71 if(current_char == '/'){ 72 token->kind = DIV_OPERATOR_TOKEN; 73 return; 74 } 75 if(current_char == '('){ 76 token->kind = LEFT_PAREN_TOKEN; 77 return; 78 } 79 if(current_char == ')') 80 { 81 token->kind = RIGHT_PAREN_TOKEN; 82 return; 83 } 84 if(isdigit(current_char)){ 85 if(status == INITIAL_STATUS) 86 { 87 status = IN_INT_PART_STATUS; 88 }else if(status == DOT_STATUS) 89 { 90 status = IN_FRAC_PART_STATUS; 91 } 92 }else if(current_char == '.') 93 { 94 if(status == IN_INT_PART_STATUS){ 95 status = DOT_STATUS; 96 }else{ 97 fprintf(stderr, "syntax error\n"); 98 exit(1); 99 } 100 }else{ 101 fprintf(stderr, "bad character(%c)\n", current_char); 102 exit(1); 103 } 104 } 105 } 106 107 108 void set_line(char* line) 109 { 110 st_line = line; 111 st_line_pos = 0; 112 } 113 114 void parse_line(char* buf) 115 { 116 Token token; 117 set_line(buf); 118 for(;;) 119 { 120 get_token(&token); 121 if(token.kind == END_OF_LINE_TOKEN){ 122 break; 123 }else{ 124 printf("kind %d, str %s\n", token.kind, token.str); 125 } 126 } 127 }

 

  //递归向下的语法分析器 parser.cpp
1
#include "stdafx.h" 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include "token.h" 5 6 #define LINE_BUFF_SIZE (1024) 7 8 static Token st_look_ahead_token; 9 static int st_look_ahead_token_exists; 10 11 //可以重复利用已经get的token(get_token本来就只能一直向下读) 12 static void my_get_token(Token* token) 13 { 14 if(st_look_ahead_token_exists) 15 { 16 *token = st_look_ahead_token; 17 st_look_ahead_token_exists=0; 18 }else{ 19 get_token(token); 20 } 21 } 22 23 //归还预读的token放到全局变量 24 static void unget_token(Token* token) 25 { 26 st_look_ahead_token = *token; 27 st_look_ahead_token_exists = 1; 28 } 29 double parse_expression(); 30 //解析一元表达式,token必须是数字 31 static double parse_primary_expressioin() 32 { 33 double value; 34 Token token; 35 int minus_flag = 0; 36 37 my_get_token(&token); 38 if(token.kind == SUB_OPERATOR_TOKEN) 39 { 40 minus_flag = 1; 41 } 42 if(token.kind == NUMBER_TOKEN) 43 { 44 value = token.value; 45 }else if(token.kind == LEFT_PAREN_TOKEN) 46 { 47 value = parse_expression(); 48 my_get_token(&token); 49 if(token.kind != RIGHT_PAREN_TOKEN) 50 { 51 fprintf(stderr, "missing ')' error\n"); 52 exit(1); 53 } 54 }else{ 55 unget_token(&token); 56 fprintf(stderr, "syntax error\n"); 57 exit(1); 58 return 0.0; 59 60 } 61 if(minus_flag == 1) 62 { 63 value = -value; 64 } 65 return value; 66 67 } 68 69 //解析乘除表达式,可以连续的乘除 70 static double parse_term() 71 { 72 double v1,v2; 73 Token token; 74 75 v1 = parse_primary_expressioin(); 76 for(;;) 77 { 78 my_get_token(&token); 79 if(token.kind != MUL_OPERATOR_TOKEN && token.kind != DIV_OPERATOR_TOKEN) 80 { 81 unget_token(&token); 82 break; 83 } 84 v2 = parse_primary_expressioin(); 85 if(token.kind == MUL_OPERATOR_TOKEN) 86 { 87 v1 *= v2; 88 }else if(token.kind == DIV_OPERATOR_TOKEN) 89 { 90 v1 /= v2; 91 } 92 } 93 return v1; 94 } 95 96 //解析加减表达式,可以连续加减 97 double parse_expression() 98 { 99 double v1,v2; 100 Token token; 101 102 v1=parse_term(); 103 for(;;) 104 { 105 my_get_token(&token); 106 if(token.kind != ADD_OPERATOR_TOKEN && token.kind != SUB_OPERATOR_TOKEN) 107 { 108 unget_token(&token); 109 break; 110 } 111 112 v2 = parse_term(); 113 if(token.kind == ADD_OPERATOR_TOKEN) 114 { 115 v1 += v2; 116 }else if(token.kind == SUB_OPERATOR_TOKEN) 117 { 118 v1 -= v2; 119 } 120 121 } 122 return v1; 123 } 124 125 double parse_line() 126 { 127 double value; 128 st_look_ahead_token_exists = 0; 129 value = parse_expression(); 130 return value; 131 } 132 133 134 int _tmain(int argc, _TCHAR* argv[]) 135 { 136 char line[LINE_BUFF_SIZE]; 137 double value; 138 139 printf("this is a LL(1) calc parser,version 1.0, copyright @ jwk.\n>"); 140 while (fgets(line, LINE_BUFF_SIZE, stdin) != NULL) 141 { 142 set_line(line); 143 value = parse_line(); 144 printf("%lf\n>", value); 145 } 146 return 0; 147 }

  这么经典的算法,有空的时候我得画个图好好演示一下。

转载于:https://www.cnblogs.com/jwk000/p/4285089.html

附录c 编译程序实验 实验目的:用c语言对一个简单语言的子集编制一个一遍扫描的编译程序,以加深对编译原理的理解,掌握编译程序的实现方法和技术。 语法分析 C2.1 实验目的 编制一个递归下降分析程序,实现对词法分析程序所提供的单词序列的语法检查和结构分析. C2.2 实验要求 利用C语言编制递归下降分析程序,并对简单语言进行语法分析. C2.2.1待分析的简单语言的语法 实验目的 通过上机实习,加深对语法制导翻译原理的理解,掌握将语法分析所识别的语法成分变换为中间代码的语义翻译方法. 实验要求 采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为三地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4;x:=(a+b)/c end# 输出的三地址指令如下: (1) t1=3*4 (2) t2=2+t1 (3) a=t2 (4) t3=a+b (5) t4=t3/c (6) x=t4 算法思想 1设置语义过程 (1) emit(char *result,char *arg1,char *op,char *ag2) 该函数功能是生成一个三地址语句送到四元式表中。 四元式表的结构如下: struct {char result[8]; char ag1[8]; char op[8]; char ag2[8]; }quad[20]; (2)char *newtemp() 该函数回送一个新的临时变量名,临时变量名产生的顺序为T1,T2,…. Char *newtemp(void) { char *p; char m[8]; p=(char *)malloc(8); k++; itoa(k,m,10); strcpy(p+1,m); p[0]=’t’; return(p); } (2)主程序示意图如图c.10所示。 (2) 函数lrparser在原来语法分析的基础上插入相应的语义动作:将输入串翻译成四元式序列。在实验中我们只对表达式、赋值语句进行翻译。 语义分析程序的C语言程序框架 int lrparser() { int schain=0; kk=0; if(syn=1) { 读下一个单词符号; schain=yucu; /调用语句串分析函数进行分析/ if(syn=6) { 读下一个单词符号; if(syn=0 && (kk==0)) 输出(“success”); } else { if(kk!=1 ) 输出 ‘缺end’ 错误;kk=1;} else{输出’begin’错误;kk=1;} } return(schain); int yucu() { int schain=0; schain=statement();/调用语句分析函数进行分析/ while(syn=26) {读下一个单词符号; schain=statement(); /调用语句分析函数进行分析/ } return(schain); } int statement() { char tt[8],eplace[8]; int schain=0; {switch(syn) {case 10: strcpy(tt,token); scanner(); if(syn=18) {读下一个单词符号; strcpy(eplace,expression()); emit(tt,eplace,””,””); schain=0; } else {输出’缺少赋值号’的错误;kk=1; } return(schain); break; } } char *expression(void) {char *tp,*ep2,*eplace,*tt; tp=(char *)malloc(12);/分配空间/ ep2=(char *)malloc(12); eplace=(char *)malloc(12); tt =(char )malloc(12); strcpy(eplace,term ());/调用term分析产生表达式计算的第一项eplace/ while(syn=13 or 14) { 操作符 tt= ‘+’或者‘—’; 读下一个单词符号; strcpy(ep2,term());/调用term分析产生表达式计算的第二项ep2/ strcpy(tp,newtemp());/调用newtemp产生临时变量tp存储计算结果/ emit(tp,eplace,tt,ep2);/生成四元式送入四元式表/ strcpy(eplace,tp); } return(eplace); } char *term(void)/仿照函数expression编写/ char *factor(void) {char *fplace; fplace=(char *)malloc(12); strcpy(fplace, “ ”); if(syn=10) {strcpy(fplace,,token);/将标识符token的值赋给fplace/ 读下一个单词符号; } else if(syn=11) {itoa(sum,fplace,10); 读下一个单词符号; } else if (syn=27) {读下一个单词符号; fplace=expression();/调用expression分析返回表达式的值/ if(syn=28) 读下一个单词符号; else{输出‘}’错误;kk=1; } } else{输出‘(’错误;kk=1; } return(fplace); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值