语法分析-LL(1)分析的python实现

语法分析:将切分的单词序列组合成各类短语短语,常见的方法:自上而下,自下而上。

LL(1):左扫描,左推导。

大体步骤:

1.从文件或其他方式导入\储存文法(实质就是几行符号流) 并把其中的终结字符和非终结字符存在数组\列表里

2.把文法每行的“或”(|)切分成两个即A->B|C 切分为A->B和A->C

for i in gramma:
    ss=i[0:1]
    j=0
    while j<len(i):
        if i[j]=='>':
            break
        j+=1
    j+=1 #找到->后的第一个位置
    while j<len(i):
        if i[j]=='\n':
            break
        if i[j]!='|':
            ss+=i[j]
        else:
            stack.append(ss)
            ss=i[0:1]
        j+=1
    stack.append(ss)

3.根据文法创建first集和follow 集

(1)first:在切分后的文法中 如果A->a…… (a为终结字符)则把a放入A的follow中 如果A->B ……(B为非终结) 则先递归求B的first,然后放入A的first

(2)follow::先把‘$’放入开始字符(E)的follow;存在 ……Ab(b为非终结),则把b放入A的follow ;存在AB(B为非终结),则把B的first放入A的follow;存在B=EAC,且C的first中含有‘ε’,则把B的follow放入A的follow;若A为某段的最后字符,则把‘$’放入A的follow;若B=……A,则把B的follow放入A的follow

def GetFirst(stack_item):
    if stack_item[1] in vt:#产生式第一个为终结
        first[stack_item[0]].add(stack_item[1])#将其放入第一个的first
    else:
        for find_item in stack:
            if find_item[0]==stack_item[1]:
                GetFirst(find_item)
                first[stack_item[0]]=first[stack_item[1]]|first[stack_item[0]]

def GetFollow(vi_item):
    for i in stack:
        j=1
        while j<len(i)-1:
            if (i[j]==vi_item)&(i[j+1] in vt): # 存在 Ab 则把b放入
                follow[vi_item].add(i[j+1])
            if (i[j]==vi_item)&(i[j+1] in vi): #存在AB 则把B的first放入
                follow[vi_item]=follow[vi_item]|first[i[j+1]]-{'ε'}
            if (i[j] in vi)&('ε' in first[i[j+1]]): #存在 A=EBC 且C的first中有空
                follow[i[j]]=follow[i[0]]|follow[i[j]]
            j+=1
        if i[len(i)-1] ==vi_item:#为某行最后一个字符
            follow[vi_item].add('$')  
        if (i[0]==vi_item)&(i[len(i)-1] in vi):#A=....B
            follow[i[len(i)-1]]=follow[vi_item]|follow[i[len(i)-1]]
        if (i[len(i)-1]==vi_item)&(i[0] in vi):
            follow[vi_item]=follow[i[0]]|follow[i[len(i)-1]]
                

4.求预测分析表(代码使用双重字典)

对于每个产生式A->alpha 执行(1)(2)

(1)对first(alpha)中的每一个终结字符a,把A->alpha放入M[A,a]

 

(2)如果ε在first(alpha)中,对于follow(alpha)的每一个终结字符b,把A->alpha放入M[A,b](包括$)

写了半天并没有写出来 于是就手动建了。。。

6.对输入的记号流进行分析:

设w为记号流 ip为指向第一个符号  设S为栈 初始只有开始元素E和$($在最低)

while(栈顶不是$){

如果 栈顶与ip指向符号相同,则栈弹出,ip指向下一个

否则如果栈顶为终结符,则出错

否则如果栈顶和ip指向符号所在的分析表不存在,则出错

否则如果存在,先输出预测表中对应的产生式,然后弹出栈顶元素,并把产生式->后的元素逆序压入栈中

 

}

完整代码:

# -*- coding: utf-8 -*-
"""
Created on Tue Jan  1 12:32:13 2019

@author: 71405
"""
from collections import defaultdict
def addtwodimdict(thedict, key_a, key_b, val): 
    if key_a in thedict:
        thedict[key_a].update({key_b: val})
    else:
        thedict.update({key_a:{key_b: val}})


def GetFirst(stack_item):
    if stack_item[1] in vt:#产生式第一个为终结
        first[stack_item[0]].add(stack_item[1])#将其放入第一个的first
    else:
        for find_item in stack:
            if find_item[0]==stack_item[1]:
                GetFirst(find_item)
                first[stack_item[0]]=first[stack_item[1]]|first[stack_item[0]]

def GetFollow(vi_item):
    for i in stack:
        j=1
        while j<len(i)-1:
            if (i[j]==vi_item)&(i[j+1] in vt): # 存在 Ab 则把b放入
                follow[vi_item].add(i[j+1])
            if (i[j]==vi_item)&(i[j+1] in vi): #存在AB 则把B的first放入
                follow[vi_item]=follow[vi_item]|first[i[j+1]]-{'ε'}
            if (i[j] in vi)&('ε' in first[i[j+1]]): #存在 A=EBC 且C的first中有空
                follow[i[j]]=follow[i[0]]|follow[i[j]]
            j+=1
        if i[len(i)-1] ==vi_item:#为某行最后一个字符
            follow[vi_item].add('$')  
        if (i[0]==vi_item)&(i[len(i)-1] in vi):#A=....B
            follow[i[len(i)-1]]=follow[vi_item]|follow[i[len(i)-1]]
        if (i[len(i)-1]==vi_item)&(i[0] in vi):
            follow[vi_item]=follow[i[0]]|follow[i[len(i)-1]]
                

vt=['i','+','*','(',')','ε'] #终结字符
vi=['E','e','T','t','F'] #非终结字符
gramma=open('gram.txt').readlines()
stack=[]
for i in gramma:
    ss=i[0:1]
    j=0
    while j<len(i):
        if i[j]=='>':
            break
        j+=1
    j+=1 #找到->后的第一个位置
    while j<len(i):
        if i[j]=='\n':
            break
        if i[j]!='|':
            ss+=i[j]
        else:
            stack.append(ss)
            ss=i[0:1]
        j+=1
    stack.append(ss)
first=defaultdict(set) #构建元素映射到多个元素(集合)的字典
follow=defaultdict(set)
for stack_item in stack:
    GetFirst(stack_item)
    
follow['E'].add('$')   
for vi_item in vi:
    GetFollow(vi_item)
ana_table=dict() #建立预测分析表 其中key为非终结符和输入符号字符串的相连接 value为对应的分析结果


addtwodimdict(ana_table, 'E', 'i', 'E->Te')
addtwodimdict(ana_table, 'E', '(', 'E->Te')
addtwodimdict(ana_table, 'e', '+', 'e->+Te')
addtwodimdict(ana_table, 'e', ')', 'e->ε')
addtwodimdict(ana_table, 'e', '$', 'e->ε')
addtwodimdict(ana_table, 'T', 'i', 'T->Ft')
addtwodimdict(ana_table, 'T', '(', 'T->Ft')
addtwodimdict(ana_table, 't', '+', 't->ε')
addtwodimdict(ana_table, 't', '*', 't->*Ft')
addtwodimdict(ana_table, 't', ')', 't->ε')
addtwodimdict(ana_table, 't', '$', 't->ε')
addtwodimdict(ana_table, 'F', 'i', 'F->i')
addtwodimdict(ana_table, 'F', ')', 'F->(E)')

    
sen="i*i+i$"
ip=0 
ss=['$','E']
while ss[len(ss)-1]!='$':
    print(ss)
    print(sen[ip])
    if ss[len(ss)-1]==sen[ip]:
        ss.pop()
        ip+=1
    elif ss[len(ss)-1] in vt:
        print("error1")
        break
    elif sen[ip] not in ana_table[ss[len(ss)-1]]:
        print("error2")
        break
    elif sen[ip] in ana_table[ss[len(ss)-1]]:
        strings=ana_table[ss[len(ss)-1]][sen[ip]]
        print(strings)
        ss.pop()
        j=len(strings)-1
        while j>2:
            if strings[j]!='ε':
                ss.append(strings[j])
            j-=1
        
        


        
        


            
        

            

            
    
    

 

使用的文法为《编译原理》高等教育出版社第三章3.8文法 其中为了方便考虑E‘表示为e T’同理

文法(gramma):

切分后(stack):->已经省略

first集与follow集合:(不知道为啥多了一行 ,但没有value ,还没来得及修改)

最后分析结果:

  • 4
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
LL(1)分析表是一种自上而下的语法分析方法,用于分析上下文无关文法。下面是一个简单的Python代码的文法: ``` <program> ::= <statement_list> <statement_list> ::= <statement> ";" <statement_list> | <statement> <statement> ::= <assignment_statement> | <print_statement> <assignment_statement> ::= <variable> "=" <expression> <print_statement> ::= "print" "(" <expression> ")" <expression> ::= <variable> | <integer_literal> ``` 接下来,我们可以使用LL(1)分析表进行语法分析: 1. 首先,我们需要构造FIRST集和FOLLOW集。 ``` FIRST(<program>) = FIRST(<statement_list>) = {<variable>, <integer_literal>, "print"} FIRST(<statement>) = FIRST(<assignment_statement>) ∪ FIRST(<print_statement>) = {<variable>, "print"} FIRST(<assignment_statement>) = FIRST(<variable>) = {<variable>} FIRST(<print_statement>) = {"print"} FIRST(<expression>) = FIRST(<variable>) ∪ FIRST(<integer_literal>) = {<variable>, <integer_literal>} FOLLOW(<program>) = {$} FOLLOW(<statement_list>) = FOLLOW(<program>) = {$} FOLLOW(<statement>) = {";", $} FOLLOW(<assignment_statement>) = {";", $} FOLLOW(<print_statement>) = {";", $} FOLLOW(<expression>) = {";", $} ``` 2. 接下来,我们需要构造LL(1)分析表。 ``` +------------------+----------------------+----------------------+----------------------+----------------------+ | | <variable> | <integer_literal> | "print" | ";" | $ | +------------------+----------------------+----------------------+----------------------+----------------------+ | <program> | <statement_list> | | <statement> | | | +------------------+----------------------+----------------------+----------------------+----------------------+ | <statement_list> | <statement> | | <statement> | | | +------------------+----------------------+----------------------+----------------------+----------------------+ | <statement> | <assignment_statement>| | <print_statement>| ";" | $ | +------------------+----------------------+----------------------+----------------------+----------------------+ | <assignment_statement>| <variable> | | | "=" | | +------------------+----------------------+----------------------+----------------------+----------------------+ | <print_statement>| | | "print" | "(" | <expression> | +------------------+----------------------+----------------------+----------------------+----------------------+ | <expression> | <variable> | <integer_literal> | | | | +------------------+----------------------+----------------------+----------------------+----------------------+ ``` 3. 然后,我们可以使用LL(1)分析表进行语法分析。 假设我们有以下代码: ``` a = 1; print(a); ``` 首先,我们将`<program>`推入分析栈中。然后,我们查看分析栈的栈顶元素,并查找LL(1)分析表中该元素以及下一个输入符号的交叉点。在这种情况下,我们查看`<program>`和`a`的交叉点。该交叉点对应的产生式是`<program> ::= <statement_list>`,因此我们将`<statement_list>`推入分析栈中。 接下来,我们查看分析栈的栈顶元素,并查找LL(1)分析表中该元素以及下一个输入符号的交叉点。在这种情况下,我们查看`<statement_list>`和`a`的交叉点。由于`<statement_list>`可以推出`<statement>`,因此我们将`<statement>`推入分析栈中。 然后,我们查看分析栈的栈顶元素,并查找LL(1)分析表中该元素以及下一个输入符号的交叉点。在这种情况下,我们查看`<statement>`和`a`的交叉点。该交叉点对应的产生式是`<assignment_statement> ::= <variable> "=" <expression>`,因此我们将`<assignment_statement>`推入分析栈中。 依此类推,我们可以使用LL(1)分析表进行语法分析。如果分析成功,则该代码是符合文法的。如果分析失败,则该代码不符合文法
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值