S属性定义的自下而上计算的实现

借鉴和优化自原博客:https://blog.csdn.net/shl_shl/article/details/53535809

由于还没有复(yu)习(xi)到这个地方所以这个实验的算法部分使用原博客的思想,但原博客存在冗余(例如建分析表、终结符的哈希值互换、规约时出栈次数等),本例使用python对其进行简化、优化和完善。方括号内为原作者的做法及程序块,括号前为改进的做法:

使用的文法和之前LR1所使用的完全相同,导入文件,存在list中【原作者:print()函数】

在这里用excel填入action表:

然后通过python的xlrd导入即可,goto表由于部分没有值所以还是麻烦一点【原作者:函数Initial()】

存储表时采用和ll1、lr1相同的结构体(双重字典)【原作者:二维数组,并采用NumOfSymbols和GetSymbol对 第二个key 终结字符进行数字间的转化】

 

输入要计算的式子:本例随便打了个(11+22)*33+44*55+66(本程序也仅限做带括号的整数加乘运算和一切异常的处理)(如果计算整数理论上对正则表达式的匹配时匹配小数点)

先把式子转化为(i+i)*i+i*i+i的形式,并把对应的数字放入数字表中,并使用指针ip指向第一个(使用正则表达式匹配数字)【原作者:50多行的函数Change()】

正题:

定义一个符号栈(以下简称栈)和一个数字栈 符号栈存状态和非终结字符,数字栈存运算过程中产生的中间结果

设当前指向((i+i)*i+i*i+i)的字符为x,符号栈的栈顶状态为a

如果action[a][x]是si,则表示移进,并把x和i入栈,x指向原串的下一个字符

如果action[a][x]是acc,则表示完成,此时数字栈中仅有一个元素且为最终结果,退出即可

如果action[a][x]是ei,则表示出某错,对错误类型就行修正后继续执行:

     e1 缺少i,入栈i和状态5

     e2 多余右括号,x指向下一个(跳过右括号)

     e3 缺少+,入栈+和状态6【原作者是入栈的+,但似乎输出里写错了】

     e4 缺少右括号,入栈右括号和状态11

如果action[a][x]是ri,则表示规约:

     出栈的次数为第i个产生式右端长度的二倍,出栈后把第i个产生式左部的符号入栈【原作者:对次数和符号进行了实例化,即         对 对应产生式的次数和符号进行了分别讨论和赋值】

     如果是r1:把数字栈的出栈两次,弹出的两个数字相加并放入数字栈

     如果是r3:把数字栈的出栈两次,弹出的两个数字相乘并放入数字栈

     如果是r6:  把数字表(正则表达式得到的)的ip指向的数字入栈,ip指向下一个

 

python代码:

# -*- coding: utf-8 -*-
"""
Created on Fri Jan  4 20:58:06 2019

@author: 71405
"""

"""
使用lr1所使用的文法和分析表
"""

import xlrd
import re
import math
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}})
    
action=dict()
goto=dict()
file="ana_table.xls"
wb=xlrd.open_workbook(file)
ws=wb.sheet_by_name('Sheet1')
#把excel中的数据导入到二维数组中
row=0#行
col=0#列
for i in range(12):
    for j in ['i','+','*','(',')','$']:
        addtwodimdict(action, i, j, ws.cell_value(row,col))
        col+=1
        if col==6:
            col=0
            row+=1
        if row==12:
            break

addtwodimdict(goto, 0, 'E', 1)
addtwodimdict(goto, 0, 'T', 2)
addtwodimdict(goto, 0, 'F', 3)
addtwodimdict(goto, 4, 'E', 8)
addtwodimdict(goto, 4, 'T', 2)
addtwodimdict(goto, 4, 'F', 3)
addtwodimdict(goto, 6, 'T', 9)
addtwodimdict(goto, 6, 'F', 3)
addtwodimdict(goto, 7, 'F', 10)

gramma=open('gram2.txt').readlines()
i=0
while i<len(gramma): #去掉'->'符号
    gramma[i]=gramma[i][0:1]+gramma[i][3:len(gramma[i])-1]
    i+=1


input_string="(11+22)*33+44*55+66$"
print("输入的计算式:   "+input_string)
stack_s=['$',0]#状态栈
stack_i=[] #放数字
stack_num=[] #放数字栈
ip=0
number=re.findall(r'[0-9]+',input_string)
changed=input_string
for i in number:
    stack_i.append(i)
    changed=changed.replace(str(i),"i")
print("转换后的字符串:     "+changed)
length=len(changed)
pro=0

while pro<length:
    i=changed[pro]
    top=stack_s[len(stack_s)-1]
    print("栈:"+str(stack_s)+"    字符"+i)
    print("数字栈:"+str(stack_num))
    if action[top][i][0]=='s':
        print("移进")
        stack_s.append(i)
        stack_s.append(int(action[top][i][1:]))
        print(action[top][i][1:]+"入栈")
        pro+=1
    elif action[top][i][0]=='r':
        print("规约"+gramma[int(action[top][i][1])-1][0]+"->"+gramma[int(action[top][i][1])-1][1:])
        ch=int(action[top][i][1:])
        time=2*len(gramma[int(action[top][i][1])-1][1:])
        sym=gramma[int(action[top][i][1])-1][0]
        prep=0
        while prep<time:
            stack_s.pop()
            prep+=1
        now_top=stack_s[len(stack_s)-1]
        print("弹栈"+str(time)+"次后,栈顶状态为"+str(now_top))
        stack_s.append(sym)
        print("放入"+sym)
        #处理goto表
        if sym in goto[now_top]:
            stack_s.append(int(goto[now_top][sym]))
            print("将状态"+str(goto[now_top][sym])+"入栈")
        if action[top][i][1]=='1':
            tmp1=stack_num[len(stack_num)-1]
            stack_num.pop()
            tmp2=stack_num[len(stack_num)-1]
            stack_num.pop()
            tmp=int(tmp1)+int(tmp2)
            stack_num.append(str(tmp))
            print("弹出"+str(tmp1)+"+"+str(tmp2)+"放入"+str(tmp))
        elif action[top][i][1]=='3':
            tmp1=stack_num[len(stack_num)-1]
            stack_num.pop()
            tmp2=stack_num[len(stack_num)-1]
            stack_num.pop()
            tmp=int(tmp1)*int(tmp2)
            stack_num.append(str(tmp))
            print("弹出"+str(tmp1)+"*"+str(tmp2)+"放入"+str(tmp))
        elif  action[top][i][1]=='6':
            stack_num.append(stack_i[ip])
            ip+=1
            print("放入"+str(stack_i[ip-1]))
    elif action[top][i][0]=='e':
        if action[top][i][1]=='1':
            print("error1:缺少i,添加")
            stack_s.append('i')
            stack_s.append(5)
        if action[top][i][1]=='2':
            print("error2:多余右括号,跳过")
            pro+=1
            continue
        if action[top][i][1]=='3':
            print("error3:缺少+,添加")
            stack_s.append('+')
            stack_s.append(6)
        if action[top][i][1]=='4':
            print("error4:缺少右括号,添加")
            stack_s.append('}')
            stack_s.append(11)            
    elif action[top][i]=="acc":
        print("结果为"+str(stack_num[len(stack_num)-1]))
        break
    else:
        print("error")
    print("------------------------------------------------------")
        
        

    

由于懒得加注释了在这对部分的变量进行解释:【变量名:类型/解释】

action/goto:二维字典 对应分析表中的action和goto

gramma:list 文件读入的文法

input_string:字符串 输入的计算式

stack_s:stack(list仿) 符号栈

stack_num:stack(list仿)存储中间结果的数字栈

stack_i: list 数字表

ip:int stack_i访问时的下标(仿指针)

number:list 数字表转化的中间表(PS:现在想想好像是多余的)

changed:string input_string把数字替换成i后的表

length:int changed长度

pro:int 访问changed的下标(仿指针)

i:char pro所在(指向)的changed的字符

top/now_top:char/int(栈内同时存在两种类型) stack_s栈顶元素

time:int 出栈次数

sym:char 对应产生式左端的符号

prep:int 迭代出栈

tmp1/tmp2:int 出数字栈所得的结果

tmp:int tmp1和tmp2运算所得的结果

 

 

 

 

 

最后运行结果:

输入的计算式:   (11+22)*33+44*55+66$
转换后的字符串:     (i+i)*i+i*i+i$
栈:['$', 0]    字符(
数字栈:[]
移进
4入栈
------------------------------------------------------
栈:['$', 0, '(', 4]    字符i
数字栈:[]
移进
5入栈
------------------------------------------------------
栈:['$', 0, '(', 4, 'i', 5]    字符+
数字栈:[]
规约F->i
弹栈2次后,栈顶状态为4
放入F
将状态3入栈
放入11
------------------------------------------------------
栈:['$', 0, '(', 4, 'F', 3]    字符+
数字栈:['11']
规约T->F
弹栈2次后,栈顶状态为4
放入T
将状态2入栈
------------------------------------------------------
栈:['$', 0, '(', 4, 'T', 2]    字符+
数字栈:['11']
规约E->T
弹栈2次后,栈顶状态为4
放入E
将状态8入栈
------------------------------------------------------
栈:['$', 0, '(', 4, 'E', 8]    字符+
数字栈:['11']
移进
6入栈
------------------------------------------------------
栈:['$', 0, '(', 4, 'E', 8, '+', 6]    字符i
数字栈:['11']
移进
5入栈
------------------------------------------------------
栈:['$', 0, '(', 4, 'E', 8, '+', 6, 'i', 5]    字符)
数字栈:['11']
规约F->i
弹栈2次后,栈顶状态为6
放入F
将状态3入栈
放入22
------------------------------------------------------
栈:['$', 0, '(', 4, 'E', 8, '+', 6, 'F', 3]    字符)
数字栈:['11', '22']
规约T->F
弹栈2次后,栈顶状态为6
放入T
将状态9入栈
------------------------------------------------------
栈:['$', 0, '(', 4, 'E', 8, '+', 6, 'T', 9]    字符)
数字栈:['11', '22']
规约E->E+T
弹栈6次后,栈顶状态为4
放入E
将状态8入栈
弹出22+11放入33
------------------------------------------------------
栈:['$', 0, '(', 4, 'E', 8]    字符)
数字栈:['33']
移进
11入栈
------------------------------------------------------
栈:['$', 0, '(', 4, 'E', 8, ')', 11]    字符*
数字栈:['33']
规约F->(E)
弹栈6次后,栈顶状态为0
放入F
将状态3入栈
------------------------------------------------------
栈:['$', 0, 'F', 3]    字符*
数字栈:['33']
规约T->F
弹栈2次后,栈顶状态为0
放入T
将状态2入栈
------------------------------------------------------
栈:['$', 0, 'T', 2]    字符*
数字栈:['33']
移进
7入栈
------------------------------------------------------
栈:['$', 0, 'T', 2, '*', 7]    字符i
数字栈:['33']
移进
5入栈
------------------------------------------------------
栈:['$', 0, 'T', 2, '*', 7, 'i', 5]    字符+
数字栈:['33']
规约F->i
弹栈2次后,栈顶状态为7
放入F
将状态10入栈
放入33
------------------------------------------------------
栈:['$', 0, 'T', 2, '*', 7, 'F', 10]    字符+
数字栈:['33', '33']
规约T->T*F
弹栈6次后,栈顶状态为0
放入T
将状态2入栈
弹出33*33放入1089
------------------------------------------------------
栈:['$', 0, 'T', 2]    字符+
数字栈:['1089']
规约E->T
弹栈2次后,栈顶状态为0
放入E
将状态1入栈
------------------------------------------------------
栈:['$', 0, 'E', 1]    字符+
数字栈:['1089']
移进
6入栈
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6]    字符i
数字栈:['1089']
移进
5入栈
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'i', 5]    字符*
数字栈:['1089']
规约F->i
弹栈2次后,栈顶状态为6
放入F
将状态3入栈
放入44
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'F', 3]    字符*
数字栈:['1089', '44']
规约T->F
弹栈2次后,栈顶状态为6
放入T
将状态9入栈
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'T', 9]    字符*
数字栈:['1089', '44']
移进
7入栈
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'T', 9, '*', 7]    字符i
数字栈:['1089', '44']
移进
5入栈
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'T', 9, '*', 7, 'i', 5]    字符+
数字栈:['1089', '44']
规约F->i
弹栈2次后,栈顶状态为7
放入F
将状态10入栈
放入55
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'T', 9, '*', 7, 'F', 10]    字符+
数字栈:['1089', '44', '55']
规约T->T*F
弹栈6次后,栈顶状态为6
放入T
将状态9入栈
弹出55*44放入2420
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'T', 9]    字符+
数字栈:['1089', '2420']
规约E->E+T
弹栈6次后,栈顶状态为0
放入E
将状态1入栈
弹出2420+1089放入3509
------------------------------------------------------
栈:['$', 0, 'E', 1]    字符+
数字栈:['3509']
移进
6入栈
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6]    字符i
数字栈:['3509']
移进
5入栈
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'i', 5]    字符$
数字栈:['3509']
规约F->i
弹栈2次后,栈顶状态为6
放入F
将状态3入栈
放入66
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'F', 3]    字符$
数字栈:['3509', '66']
规约T->F
弹栈2次后,栈顶状态为6
放入T
将状态9入栈
------------------------------------------------------
栈:['$', 0, 'E', 1, '+', 6, 'T', 9]    字符$
数字栈:['3509', '66']
规约E->E+T
弹栈6次后,栈顶状态为0
放入E
将状态1入栈
弹出66+3509放入3575
------------------------------------------------------
栈:['$', 0, 'E', 1]    字符$
数字栈:['3575']
结果为3575

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值