词法分析(字符串分析)

词法分析是编译器实现的第一步。主要是分析输入的源程序(字符串),输出该字符串中出现的所有的合法的单词。例如:int a = 3 + 5;经过词法分析会输出 int,a,=,3,+,5和;这七个单词。实现词法分析器的官方做法是:
    1.写出各个单词的正规式(正则表达式);
    2.根据正规式构造NFA(不确定的有限自动机);
    3.将NFA转换DFA(确定的有限自动机);
    4.根据DFA就可以实现词法分析器,写出程序。

下面用实例来说明上面的各个步骤:
    假设我们要实现一个很简单的脚本,该脚本中只有两种类型的单词,一种就是变量,变量名的规则就是以字母开头后面紧跟0个或多个字母或数字的的字符串,如 a 和 a12d3 等;另一种就是操作符,很简单只有 &,|,~,(,),==,!= 这几个。给出几个脚本的实例:result1 & result2,rst1&(rst2|rst3),answer1 | (~answer2)。

按照上面的步骤,让我们来看一下如何实现这个词法分析器:
第一步:写出正规式
    变量的正规式是:letter(letter|digit) *
    操作符的正规式:&,|,~,(,)。由于操作符都是固定字符的,所以正规式就是它本身。
第二步:根据正规式构造NFA
    正规式构造NFA的基础是先构造正规式中每个字符的NFA,如变量的正规式中只有两个字符 letter 和 digit,他们的正规式分别是:
                 
根据构造|形式的NFA的公式可以构造 letter|digit 的NFA如下图:

(letter|digit) *的NFA为:

letter(letter|digit) *的NFA为:

第三步:将NFA转换为DFA
    先是将所有通过 ε可以到达的状态合并,由上图NFA可以看出1,2,3,4,6,9都是通过 ε可以直接用箭头连接的,以此类推5,8,9,3,4,6和7,8,9,3,4,6都是可以合并称一个状态的,如下图:

此图用新的DFA表示如下:

   由于生成的DFA的状态过多,需要将上面的DFA最小化,生成状态数最少的DFA,最小化DFA的过程就是将那些无论怎样转换都仍然转换到本组合内部的状 态组合合并,如上图{B,C,D}这三个状态组合称状态组无论经过letter还是digit转换都仍然转换到该组合,那么就可以将这三个状态合并,如下 图:
        用新状态表示为:      

脚本中变量的DFA已经构造好了。
由于操作符都是固定字符,所以DFA比较简单,下面给出几个的图示例子:




现在我们将各个单词的DFA组合在一起,大致类似下图:

实 际上,由于这种很简单的例子不必套用这种正规的步骤来得到DFA,可以直接把图画出来。首先画一个开始状态(Start)和一个结束状态(Done),然 后再考虑各个单词之间的关系将其分类,这种分类的过程就看你的经验了,依据各个单词挨个字符被识别的过程即可画出类似上图的DFA,然后再根据这个DFA 写出词法分析的程序来就非常的简单了。
下面给出根据这个DFA的写出的大概程序,这段代码可执行,但是没有考虑各种意外、出错以及相关优化处理等,所以仅供参考,请斟酌使用:
  1 
  2  public   class  ScriptScanner
  3  {
  4     private  String scriptInput  =   null ;
  5 
  6     /**
  7     *  @param  scriptInput
  8      */
  9     public  ScriptScanner(String scriptInput)
 10    {
 11       this .scriptInput  =  scriptInput;
 12    }
 13 
 14     /**  标记当前读取段的开始位置  */
 15     private   int  start_read_pos  =   0 ;
 16 
 17     /**  当前位置  */
 18     private   int  current_pos  =   0 ;
 19 
 20     private   char  readChar()
 21    {
 22       if  (scriptInput  ==   null   ||  current_pos  >=  scriptInput.length())
 23         return  EOF;
 24 
 25       return  scriptInput.charAt(current_pos);
 26    }
 27 
 28     public  Token getNextToken()
 29    {
 30      Token currentToken  =   null ;
 31 
 32       int  state  =  STATE_START;
 33 
 34      start_read_pos  =  current_pos;
 35 
 36       while  (state  !=  STATE_DONE)
 37      {
 38         char  ch  =  readChar();
 39 
 40         switch  (state)
 41        {
 42           case  STATE_START:
 43          {
 44             if  (Character.isLetter(ch))
 45            {
 46              state  =  STATE_IN_VAR;
 47            }
 48             else   if  (ch  ==   ' = ' )
 49            {
 50              state  =  STATE_IN_EQUAL;
 51            }
 52             else   if  (ch  ==   ' < ' )
 53            {
 54              state  =  STATE_IN_NOTEQUAL;
 55            }
 56             else
 57            {
 58              state  =  STATE_DONE;
 59 
 60               switch  (ch)
 61              {
 62                 case  EOF:
 63                  currentToken  =   new  Token(TokenType.EOF);
 64                   break ;
 65 
 66                 case   ' & ' :
 67                  currentToken  =   new  Token(TokenType.AND);
 68                   break ;
 69 
 70                 case   ' | ' :
 71                  currentToken  =   new  Token(TokenType.OR);
 72                   break ;
 73 
 74                 case   ' ~ ' :
 75                  currentToken  =   new  Token(TokenType.NOT);
 76                   break ;
 77 
 78                 case   ' ( ' :
 79                  currentToken  =   new  Token(TokenType.LPAREN);
 80                   break ;
 81 
 82                 case   ' ) ' :
 83                  currentToken  =   new  Token(TokenType.RPAREN);
 84                   break ;
 85 
 86                 default :
 87                  currentToken  =   new  Token(TokenType.ERROR,  " 无法识别的字符 " );
 88                   break ;
 89              }
 90 
 91            }  //  End: else
 92 
 93            current_pos  ++ //  开始状态下除 EOF 外都需要将位置后移
 94 
 95             break ;
 96 
 97          }  //  End: case STATE_START
 98 
 99           case  STATE_IN_EQUAL:
100          {
101            state  =  STATE_DONE;
102 
103             if  (ch  ==   ' = ' )
104            {
105              currentToken  =   new  Token(TokenType.EQUAL);
106            }
107             else
108            {
109              currentToken  =   new  Token(TokenType.ERROR,  " 错误的运算符 " );
110            }
111 
112            current_pos  ++ ;
113 
114             break ;
115 
116          }  //  End: case STATE_IN_EQUAL
117 
118           case  STATE_IN_NOTEQUAL:
119          {
120            state  =  STATE_DONE;
121 
122             if  (ch  ==   ' > ' )
123            {
124              currentToken  =   new  Token(TokenType.NOTEQUAL);
125            }
126             else
127            {
128              currentToken  =   new  Token(TokenType.ERROR,  " 错误的运算符 " );
129            }
130 
131            current_pos  ++ ;
132 
133             break ;
134 
135          }  //  End: case STATE_IN_NOTEQUAL
136 
137           case  STATE_IN_VAR:
138          {
139             if  ( !  Character.isLetterOrDigit(ch))
140            {
141              state  =  STATE_DONE;
142 
143              String value  =  scriptInput.substring(start_read_pos, current_pos);
144 
145              currentToken  =   new  Token(TokenType.ID, value);
146            }
147             else
148            {
149              current_pos  ++ ;
150            }
151 
152             break ;
153 
154          }  //  End: case STATE_IN_VAR
155 
156           default :
157          {
158            state  =  STATE_DONE;
159 
160            currentToken  =   new  Token(TokenType.ERROR);
161          }
162        }  //  End: switch (state)
163      }
164 
165       return  currentToken;
166    }
167 
168     public   final   static   char  EOF  =   ' /0 ' ;
169 
170     /*
171     * 定义 DFA 的状态。
172      */
173 
174     /**  开始状态  */
175     public   final   static   int  STATE_START  =   0 ;
176 
177     /**  当前 Token 是变量  */
178     public   final   static   int  STATE_IN_VAR  =   1 ;
179 
180     /**  当前 Token 是 "=="  */
181     public   final   static   int  STATE_IN_EQUAL  =   2 ;
182 
183     /**  当前 Token 是 "<>"  */
184     public   final   static   int  STATE_IN_NOTEQUAL  =   3 ;
185 
186     /**  当前 Token 结束  */
187     public   final   static   int  STATE_DONE  =   4 ;
188  }
189 
从代码中可以看出,预先根据DFA定义了5个状态:
STATE_START, STATE_IN_VAR, STATE_IN_EQUAL, STATE_IN_NOTEQUAL, STATE_DONE,然后每个记号(Token)都是从 STATE_START开始到 STATE_DONE结束,中间根据输入字符的不同在各个状态中不断的转换,直到识别出记号或者错误为止。由此可见只要画好DFA写代码就简单多了。
    有兴趣的朋友可以研究一下语法分析,生成语法树,根据语法树求值;也可以研究一下利用中缀表达式求值。

http://www.blogjava.net/qujinlong123/
 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验2 语法分析 一、 实验目的 编制一个递归下降分析程序,实现对词法分析程序所提供的单词序列的语法检查和结构分析。 二、 实验内容 利用C语言编制递归下降分析程序,并对简单语言进行语法分析。 1、 待分析的简单语言的语法 用扩充的BNF表示如下: (1)::=beginend (2)::={;} (3)::= (4)::=ID:= (5)::={+|-} (6)::={*|/} (7)::=ID| NUM|() 2、实验要求说明 输入单词,以“#”结束,如果是文法正确的句子,则输出成功信息,打印“success”,否则输出“error”。 三、 结果验证 1. 输入:begin_a:=9;_x:=2*3;_b:=a+x_end# 输出:success! 2. 分别验证其他错误(至少2个) 四、 语法分析程序的源程序代码 #include #include #include char prog[80],token[8]; char ch; int syn,p,m=0,n,sum=0,kk; //p是缓冲区prog的指针,m是token的指针 char *rwtab[6]={"begin","if","then","while","do","end"}; void scaner(); void factor(); void term(); void expression(); void statement(); #include #include #include char prog[80],token[8]; char ch; int syn,p,m=0,n,sum=0; //p是缓冲区prog的指针,m是token的指针 char *rwtab[6]={"begin","if","then","while","do","end"}; void scaner() { 同实验一代码 } void factor() { …完成函数代码 } void term() { ……完成函数代码 } void expression() { ……完成函数代码 } void statement() {//cout<<"调用statement,syn="<<syn<<endl; if(判断语句) {scaner(); if(syn==18) {//cout<<"syn=18"<<endl; scaner(); expression(); } else{cout<<"缺:=错误!"<<endl;kk=1;} } else {cout<<"error!"<<endl;kk=1;} return; } void yucu() {…..完成函数代码 } //return; } void lrparser() {//cout<<"调用Irparser"<<endl; if(syn==1) {scaner(); yucu(); if(syn==6){scaner(); if(syn==0&&(kk==0)) cout<<"success!"<<endl; } else {if(kk!=1) cout<<"缺end错误!"<<endl;kk=1;} } else{cout<<"缺begin错误!"<<endl;kk=1;} //return; } void main() {p=0; cout<>ch; 执行语句3; }while(ch!='#'); p=0; scaner(); lrparser(); //cout<<syn<<','<<kk<<endl; }
以下是 Java 词法分析器识别字符的示例代码: ```java public class Lexer { private String input; // 输入的代码 private int position; // 当前扫描的位置 public Lexer(String input) { this.input = input; this.position = 0; } // 判断一个字符是否是字母 private boolean isLetter(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } // 判断一个字符是否是数字 private boolean isDigit(char c) { return c >= '0' && c <= '9'; } // 识别一个字符字面量 private Token recognizeChar() { char c = input.charAt(position); if (c == '\'') { // 找到下一个单引号 int end = input.indexOf('\'', position + 1); if (end != -1) { // 取出字符字面量并返回 Token String s = input.substring(position, end + 1); position = end + 1; return new Token(TokenType.CHARACTER, s); } else { // 没有找到匹配的单引号,认为是错误 String s = input.substring(position); position = input.length(); return new Token(TokenType.ERROR, "Unmatched single quote: " + s); } } else { // 不是单引号开头,认为是错误 String s = input.substring(position); position = input.length(); return new Token(TokenType.ERROR, "Invalid character: " + s); } } // 获取下一个 Token public Token nextToken() { while (position < input.length()) { char c = input.charAt(position); if (isLetter(c)) { // 识别标识符或关键字 return recognizeIdentifierOrKeyword(); } else if (isDigit(c)) { // 识别数字字面量 return recognizeNumber(); } else if (c == '\'') { // 识别字符字面量 return recognizeChar(); } else { // 识别其他符号 return recognizeSymbol(); } } // 到达输入的末尾,返回结束符 return new Token(TokenType.EOF, ""); } } ``` 在这个示例代码,`Lexer` 类是一个简单的词法分析器,用于扫描输入的代码并识别各种语言元素。其,`recognizeChar` 方法用于识别字符字面量,它会读取输入的字符流并判断其的单引号,从而识别出一个完整的字符字面量。在 `nextToken` 方法,当扫描到一个单引号时,会调用 `recognizeChar` 方法来识别字符字面量。如果识别成功,会返回一个 `Token` 对象,其的 `TokenType` 是 `CHARACTER`,并且 `Token` 的值为字符字面量的字符串表示。如果识别失败,会返回一个 `Token` 对象,其的 `TokenType` 是 `ERROR`,并且 `Token` 的值为错误信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值