【编译原理】基于词法分析器的LL1语法分析器

【编译原理】基于词法分析器的LL1语法分析器

实验要求

设计一个满足以下要求的⽂法:
(1)识别只包含变量声明语句和执行语句程序段的语法结构合法性;
(2)变量声明中只使用int,char,float 3类基本类型;
(3)变量声明中允许初始化,每一个数据类型可以声明多个变量;
(4)执行语句只包括:表达式语句、if语句、while语句;
(5)可以有多条变量声明语句和执行语句;
(6)没有函数声明,所有语句都在同一 个函数体内。
(7)表达式是以=、+、-、*、/、>、<为运算符,常数、变量标识符为操作数,并带圆括号的式子。

对满足以上要求的文法构造其相应分析表(LL或LR);对于给定输⼊符号串,能检查其是否语法正确;若有错,显示出错语句所在行及简单错误信息。
注意:要求以词法分析的结果,即单词表中的编码为输入内容。

评分标准

①分析表是否是自动生成;
②是否能识别多条语句;
③是否能识别声明语句;
④是否能识别执行语句,包括if、while、表达式语句;
⑤每个错误是否有错误提示,包括错误所在行、错误信息;
⑥错误提示信息是否区分不同类别;

源码

grammarAnalysis.py

import re# 词法分析
class MyToken:def __init__(self, MyType, seman, MyStr):self.MyType = MyTypeself.seman = semanself.MyStr = MyStrdef __str__(self):s = self.MyStr.replace('\n','')return "token.class:  {:5}\t\tstr:  {:10}\tseman:\t{} \t".format(self.MyType, s, self.seman)# 单词集状态列表
states = ['if', 'else', 'for', 'while', 'break', 'return', 'continue', 'float', 'int', 'char', '标识符','正整数、正实数、零', '+', '-','*', '/', '%', '>', '>=', '<', '<=', '!=', '==', '!', '&', '|', ',', '=', '[', ']', '(', ')', '{', '}', ';','.', '\\', '#']
choiceStatec = [13, 14, 15, 16, 17, 18, 20, 24, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]  # 用于防止while循环中出现多次elif
inputs = []  # 输入的字符tokens = []  # 单词表
NameL = []  # 名字表
ConstL = []  # 常数表i = 0
line = 1# 将文件内容提取到inputs
def codeToInputs(sourceCodes):for line in sourceCodes:for char in line:inputs.append(char)if char == '#':return# 判断是否为字母
def isLetter(char):pattern = r"[a-zA-Z]"return bool(re.match(pattern, char))# 判断是否为数字
def isDigit(char):pattern = r"[0-9]"return bool(re.match(pattern, char))# 普通的字符识别
def isC(char, state):tokens.append(MyToken(state, "^", char))def choiceState(char):  # 选择单字符对应状态state = 0if char in states:for k in range(len(states)):if states[k] == char:  # 判断单个字符if char in ['>', '<', '!', '=', '&', '|', '\\'] and inputs[i] in ['=', '&', '|', 'n']:  # 如果是双字符state = 19else:state = k + 1else:state = 0return statedef error():print(f"第{line}行---未知字符错误")exit(0)# 词法分析
def nextToken():global iglobal linename = ""char = inputs[i]i += 1while char == ' ' or char == '\n' or char == '\t':  # 如果是空就跳过if char == '\n':line += 1char = inputs[i]i += 1if char == '#':i -= 1global statestate = 0while True:if state == 0:  # 选择if char == '#':i -= 1state = 38continueif char == '/' and (inputs[i] == '/' or inputs[i] == '*'):  # 注释state = 42continueif choiceState(char) != 0:  # 判断后面的单字符,获取状态state = choiceState(char)i -= 1continueif char == "i":state = 1elif char == "e":state = 2elif char == 'f':state = 3elif char == 'w':state = 4elif char == 'b':state = 5elif char == 'r':state = 6elif char == 'c':state = 7elif isDigit(char):  # 数字state = 12elif isLetter(char):  # 字符串i -= 1state = 11else:error()  # 非法字符报错i -= 1state = 38elif state == 1:  # ifif inputs[i] == 'f':  # 判断第二个是否为fif isDigit(inputs[i + 1]) or isLetter(inputs[i + 1]):  # 标识符i -= 1state = 11else:m = MyToken(1, "^", "if")  # 在单词表和名字表加入tokens.append(m)state = 38elif inputs[i] == 'n':  # 判断第二个是否为ni += 1if inputs[i] == 't':if isDigit(inputs[i + 1]) or isLetter(inputs[i + 1]):  # 标识符i -= 2state = 11else:m = MyToken(9, "^", "int")  # 在单词表和名字表加入tokens.append(m)state = 38else:if isDigit(inputs[i]) or isLetter(inputs[i]):  # 标识符i -= 2state = 11else:m = MyToken(11, "^", "in")  # 在单词表和名字表加入tokens.append(m)state = 38i -= 1else:  # 以i开头的标识符i -= 1state = 11elif state == 2:  # elseif ''.join(inputs[i:i + 3]) == 'lse':if isDigit(inputs[i + 3]) or isLetter(inputs[i + 3]):i -= 1state = 11else:m = MyToken(2, "^", "else")  # 在单词表和名字表加入tokens.append(m)state = 38i += 2else:i -= 1state = 11elif state == 3:  # forif inputs[i] == 'o':if inputs[i + 1] == 'r':if isDigit(inputs[i + 2]) or isLetter(inputs[i + 2]):i -= 1state = 11else:m = MyToken(3, "^", "for")  # 在单词表和名字表加入tokens.append(m)state = 38i += 1else:i -= 1state = 11elif inputs[i] == 'l':if "".join(inputs[i:i + 4]) == 'loat':if isDigit(inputs[i + 4]) or isLetter(inputs[i + 4]):i -= 1state = 11else:m = MyToken(8, "^", "float")  # 在单词表和名字表加入tokens.append(m)state = 38i += 3else:i -= 1state = 11else:i -= 1state = 11elif state == 4:  # whileif "".join(inputs[i:i + 4]) == 'hile':if isDigit(inputs[i + 4]) or isLetter(inputs[i + 4]):i -= 1state = 11else:m = MyToken(4, "^", "while")  # 在单词表和名字表加入tokens.append(m)state = 38i += 3else:i -= 1state = 11elif state == 5:  # breakif "".join(inputs[i:i + 4]) == 'reak':if isDigit(inputs[i + 4]) or isLetter(inputs[i + 4]):i -= 1state = 11else:m = MyToken(5, "^", "break")  # 在单词表和名字表加入tokens.append(m)state = 38i += 3else:i -= 1state = 11elif state == 6:  # returnif "".join(inputs[i:i + 5]) == 'eturn':if isDigit(inputs[i + 5]) or isLetter(inputs[i + 5]):i -= 1state = 11else:m = MyToken(6, "^", "return")  # 在单词表和名字表加入tokens.append(m)state = 38i += 4else:i -= 1state = 11elif state == 7:  # continueif inputs[i] == 'o':if "".join(inputs[i + 1:i + 7]) == 'ntinue':if isDigit(inputs[i + 7]) or isLetter(inputs[i + 7]):i -= 1state = 11else:m = MyToken(7, "^", "continue")  # 在单词表和名字表加入tokens.append(m)state = 38i += 6else:i -= 1state = 11elif inputs[i] == 'h':if "".join(inputs[i + 1:i + 3]) == 'ar':if isDigit(inputs[i + 3]) or isLetter(inputs[i + 3]):i -= 1state = 11else:m = MyToken(10, "^", "char")  # 在单词表和名字表加入tokens.append(m)state = 38i += 2else:state = 11i -= 1else:i -= 1state = 11elif state == 11:  # 标识符char = inputs[i]i += 1name += charif isLetter(char):state = 39else:state = 40elif state == 12:name += charchar = inputs[i]if isDigit(inputs[i]):  # 判断数字i += 1state = 12else:if inputs[i] == ' ' or inputs[i] == '\n' or inputs[i] == '\t' or inputs[i] == '#':flag = 0index = -1for ii in range(len(ConstL)):if ConstL[ii].MyStr == name:flag = 1index = iiif flag == 1:tokens.append(MyToken(12, index, name))else:ConstL.append(MyToken(12, len(ConstL), name))tokens.append(MyToken(12, len(ConstL)-1, name))else:print(f"第{line}行---标识符错误")char = inputs[i]while char != ' ' and char != '\t' and char != '\n' and char != '#':i += 1char = inputs[i]state = 38i -= 1elif state == 19:  # 处理双字符m = ""if inputs[i] == '>' and inputs[i + 1] == '=':m = MyToken(19, "^", ">=")if inputs[i] == '<' and inputs[i + 1] == '=':m = MyToken(21, "^", "<=")if inputs[i] == '!' and inputs[i + 1] == '=':m = MyToken(22, "^", "!=")if inputs[i] == '=' and inputs[i + 1] == '=':m = MyToken(23, "^", "==")if inputs[i] == '&' and inputs[i + 1] == '&':m = MyToken(25, "^", "&&")if inputs[i] == '|' and inputs[i + 1] == '|':m = MyToken(26, "^", "||")if inputs[i] == '\\' and inputs[i + 1] == 'n':m = MyToken(37, "^", "\\n")tokens.append(m)i += 1returnelif state == 38:  # 文件结束returnelif state == 39:  # 处理标识符char = inputs[i]i += 1if char == '#':i -= 1state = 40continueif isDigit(char) or isLetter(char):state = 39if char != '\n':name += charelse:line += 1else:  # 不是字母和数字,就不是标识符了state = 40elif state == 40:flag = 0index = -1for ii in range(len(NameL)):if NameL[ii].MyStr == name:flag = 1index = iiif flag == 1:tokens.append(MyToken(11, index, name))else:NameL.append(MyToken(11, len(NameL), name))tokens.append(MyToken(11, len(NameL)-1, name))i -= 2  # 外面的while+1,所以这里要-1if char == '#':i += 1try:if states.index(char) and char != '#':  # 如果有字符name = name[:-1]state = 41except:passreturnelif state == 41:st = states.index(char)isC(char, st)i -= 1returnelif state == 42:  # 处理注释 // /**/if inputs[i] == '/':  # //注释i += 1char = inputs[i]while char != '\n':i += 1char = inputs[i]i += 1state = 0passelif state in choiceStatec:isC(states[state - 1], state)  # 普通的字符识别returnelse:passdef init():LLTable['XIANGWEI'][';'] = [('XIANGWEI', 'ε')]LLTable['INITIALIZATION'][';'] = [('INITIALIZATION','ε')]# 从文件读取内容
def readSourceCode(read_path):with open(read_path, "r") as file:lines = file.readlines()return lines# 语法分析def calculateFirst(ch):# print(ch)if ch in grammar['terminals']:return set(ch)first_sets = {symbol: set() for symbol in grammar['non_terminals']}if ch not in first_sets:first_sets[ch] = set()updated = Truewhile updated:updated = Falsefor production in grammar['productions']:left_symbol = production[0]right_symbols = production[1:]# 处理右部的每个符号for right_symbol in right_symbols:if right_symbol in grammar['terminals']:# 终结符,将其添加到左部非终结符的 FIRST 集合中if right_symbol not in first_sets[left_symbol]:first_sets[left_symbol].update([right_symbol])updated = Truebreakelif right_symbol in grammar['non_terminals']:# 非终结符,将其 FIRST 集合(除去空字符)添加到左部非终结符的 FIRST 集合中symbol_first = first_sets[right_symbol]if 'ε' not in symbol_first:if symbol_first - first_sets[left_symbol]:first_sets[left_symbol].update(symbol_first)updated = Truebreakelse:if symbol_first - first_sets[left_symbol]:first_sets[left_symbol].update(symbol_first - {'ε'})updated = Truebreakelse:# 如果右部的所有符号都能推导出空字符,将空字符添加到左部非终结符的 FIRST 集合中if 'ε' not in first_sets[left_symbol]:first_sets[left_symbol].add('ε')updated = Truereturn first_sets[ch]# 计算Follow集
def calculateFollow(startSymbol):# 初始化非终结符follow集follow_sets = {}for symbol in grammar['non_terminals']:follow_sets[symbol] = set()# 文法开始符添加 #follow_sets[startSymbol].add('#')# update用于观察是否需要继续更新,用于多次循环产生式,防止某个follow集计算不完全update = Truewhile update:update = False# 遍历产生式for production in grammar['productions']:# 左侧非终结符non_terminal = production[0]# 产生式右侧symbols = production[1:]# 遍历右侧for index, symbol in enumerate(symbols):# 只有右侧非终结符才要计算Follow集if symbol in grammar['non_terminals']:# 不是右侧最后一个if index < len(symbols) - 1:first_beta = set()# 遍历非终结符后的元素,求它的first集for j in range(index + 1, len(symbols)):currentSymbol = symbols[j]currentFirst = calculateFirst(currentSymbol)first_test = follow_sets[symbol]# 非终结符后面的first集包含εif 'ε' in currentFirst:# first集还有其他元素if len(currentFirst) != 1:# 将first集除ε后添加到follow集中follow_sets[symbol].update(currentFirst - {'ε'})# 更新了follow集if first_test != follow_sets[symbol]:update = Truebreakelse:# first只有一个εfirst_beta.update({'ε'})else:# first没有εfirst_test = follow_sets[symbol]follow_sets[symbol].update(currentFirst - {'ε'})# 更新了follow集if first_test != follow_sets[symbol]:update = Truebreak# 如果非终结符右侧first为ε,那么加入左侧的follow给它if 'ε' in first_beta:# 这里很坑,不能直接将set集合复制,需要使用copy()函数,# 否则引用first_beta和follow_sets[symbol]同一个地址***first_beta = follow_sets[symbol].copy()follow_sets[symbol].update(follow_sets[non_terminal])if first_beta != follow_sets[symbol]:update = True# 右侧只有一个终结符elif index == len(symbols) - 1:first_beta = follow_sets[symbol].copy()follow_sets[symbol].update(follow_sets[non_terminal])if first_beta != follow_sets[symbol]:update = Truereturn follow_sets# 初始化并填充LL1预测分析表
def initLLTable():LLTable = {}# 初始化LL1分析表for non_terminal in grammar['non_terminals']:LLTable[non_terminal] = {}for terminal in grammar['terminals']:LLTable[non_terminal][terminal] = []LLTable[non_terminal]['#'] = []# 填充LL(1)分析表for production in grammar['productions']:non_terminal = production[0]right_symbols = production[1:]# 遍历产生式右侧的符号for symbol in right_symbols:# 如果是终结符,说明first集就是该终结符,直接在分析表中添加if symbol in grammar['terminals']:LLTable[non_terminal][symbol].append(production)break# 如果是非终结符elif symbol in grammar['non_terminals']:# 计算first集symbol_first = first_sets[symbol]for terminal in symbol_first:# 不为空if terminal != 'ε':LLTable[non_terminal][terminal].append(production)if 'ε' in symbol_first:for terminal in follow_sets[non_terminal]:LLTable[non_terminal][terminal].append(production)break# 如果产生式右部只有一个ε,说明需要加入左部follow集elif symbol == 'ε':for terminal in follow_sets[non_terminal]:LLTable[non_terminal][terminal].append(production)# print(LLTable['INITIALIZATION'])return LLTable# 栈
class stack:def __init__(self):self.top = 0self.data = []self.ch = ''  # 存储出栈的字符# 入栈def push(self, c):self.data.append(c)self.top += 1# 出栈def pop(self):if self.top == 0:print("stack is empty!")self.top -= 1self.ch = self.data.pop()# 读取栈顶元素def read(self):return self.data[self.top-1]def LLError():print("string is false!")exit(0)def vn2int(cc):  # 非终结符定位LL分析表for non_terminal in grammar['non_terminals']:if non_terminal == cc:return non_terminalelse:print("character is false!")exit(0)def vt2int(cc):  # 终结符定位LL分析表for terminal in grammar['terminals']:if terminal == cc:return terminalelse:if cc == '#':return '#'else:print("character is false!")exit(0)# 判断LL1文法的关键函数
def LL_driver():global LLStack,stringsic = inputs.read()  # 当前识别的字符sc = sem.read()  # 文法中的字符while sc != '#':# print(f"ic {ic}  ----  sc {sc}")# 是终结符if sc in grammar['terminals']:if ic == sc:  # 终结符相等就出栈# print(f"ic {ic}  --terminals--  sc {sc}")outputStrings = " ".join(strings)print(f"{LLStack:20}   {outputStrings:20} '{ic}' 匹配")chlen = len(ic)LLStack = LLStack[:-1*chlen]strings = strings[1:]inputs.pop()sem.pop()else:LLError()# 非终结符elif sc in grammar['non_terminals']:# 获得LL1分析表中的数值来执行相应算法# print(vn2int(sc) + "---" + vt2int(ic))# print(vn2int(sc)+" "+str(LLTable[vn2int(sc)]))productions = LLTable[vn2int(sc)][vt2int(ic)]# 如果产生式的列表为空,代表源输入串有误if not productions:print("[语法错误] 类型: " + vt2int(ic) + " 前存在语法错误")exit(0)# print("产生式为空~~~")# LLError()# pass# 遍历分析表中同一格的多个产生式for LLProduction in productions:for production in grammar['productions']:# 如果分析表的产生式与文法中产生式相等if LLProduction == production:non_terminal = production[0]right_symbols = production[1:]productionStr = non_terminal+"->"+"".join(right_symbols)outputStrings = " ".join(strings)print(f"{LLStack:20}   {outputStrings:20} {productionStr:20}")chlen = len(non_terminal)LLStack = LLStack[:-1*chlen]sem.pop()for right_symbol in reversed(right_symbols):sem.push(right_symbol)LLStack += right_symbolbreakelif sc == 'ε':LLStack = LLStack[:-1]outputStrings = " ".join(strings)print(f"{LLStack:20}   {outputStrings:20} ε")sem.pop()sc = sem.read()continue# else:#     LLError()ic = inputs.read()  # 当前识别的字符sc = sem.read()  # 文法中的字符if ic == '#' and sc == '#':print("[success]识别成功!")else:LLError()# 从文件中读取文法
def read_grammar_from_file(file_path):grammar = {'terminals': set(), 'non_terminals': set(), 'productions': []}with open(file_path, 'r', encoding='utf-8') as file:for line in file:line = line.strip()if not line or line.startswith('#'):continue  # 跳过空行和注释production_parts = line.split('->')if len(production_parts) != 2:raise ValueError(f"不合法产生式: 第{line}行")left_symbol = production_parts[0].strip()right_part = production_parts[1].strip()# 支持以 '|' 分隔的多个产生式right_symbols_list = [symbol.strip().split() for symbol in right_part.split('|')]chars = {';', '=', '+', '-', '*', '/', '>', '<', '{', '}', '(', ')', ','}# 将每个右侧部分作为单独的产生式添加到 grammar['productions']for right_symbols in right_symbols_list:grammar['productions'].append((left_symbol, *right_symbols))grammar['non_terminals'].add(left_symbol)grammar['terminals'].update(symbol for symbol in right_symbols if symbol.isalpha() and symbol.islower() and symbol != 'ε')grammar['terminals'].update(symbol for symbol in right_symbols if symbol in chars)if grammar['productions']:grammar['start_symbol'] = grammar['productions'][0][0]else:raise ValueError("文法文件为空!")return grammar# 打印First集
def printFirst(grammar):# 计算 First 集global first_setsfirst_sets = {}print("----------------First集----------------")for c in grammar['non_terminals']:first_sets[c] = calculateFirst(c)print("First(" + c + ")=" + str(first_sets[c]))# 打印Follow集
def printFollow(grammar):# 计算 Follow 集global follow_setsfollow_sets = calculateFollow(start_symbol)print("----------------Follow集----------------")for symbol, follow_set in follow_sets.items():formatted_set = ', '.join(sorted(follow_set))  # 将集合排序并转换为逗号分隔的字符串print(f"Follow({symbol})={{ {formatted_set} }}")def printLLTable(LLTable):# 获取所有非终结符和终结符non_terminals = list(LLTable.keys())terminals = set()for row in LLTable.values():terminals.update(row.keys())# 打印表头print("-"*50+"LL1预测分析表"+"-"*50)print(f"{'非终结符':<10}", end=' ')for terminal in terminals:print(f"{terminal:<20}", end=' ')print()# 打印分析表for non_terminal in non_terminals:print(f"{non_terminal:<10}", end=' ')for terminal in terminals:productions = LLTable[non_terminal].get(terminal, [])if productions:leftmost_production = productions[0]  # 取第一个产生式leftmost_str = leftmost_production[0]+"->"+"".join(leftmost_production[1:])print(f"{leftmost_str:<20}", end=' ')else:print(f"{'':<20}", end=' ')print()grammarDict = {'1': 'if','2': 'else','4': 'while','8': 'float','9': 'int','10': 'char','11': 'identifier','12': 'intconstant','13': '+','14': '-','15': '*','16': '/','18': '>','19': '>=','20': '<','21': '<=','22': '!=','23': '==','24': '!','25': '&&','26': '||','27': ',','28': '=','29': '[','30': ']','31': '(','32': ')','33': '{','34': '}','35': ";",'38': '#'
}if __name__ == '__main__':read_path = 'source_code.txt'sourceCodes = readSourceCode(read_path)codeToInputs(sourceCodes)while inputs[i] != '#':nextToken()i += 1tokens.append(MyToken(38, "^", "#"))  # 最后一个ch = ""     # 输入串(存储词法分析的结果)for token in tokens:ch += grammarDict.get(str(token.MyType), "") + " "inputs = stack()  # 存储待识别字符串sem = stack()  # 存储文法index = 0# 从文件中读取产生式file_path = 'file.txt'  # 替换为你的文件路径grammar = read_grammar_from_file(file_path)# 存储文法开始符号start_symbol = grammar['start_symbol']first_sets = {}follow_sets = {}printFirst(grammar)  # 计算并打印First集printFollow(grammar)  # 计算并打印Follow集LLTable = initLLTable()printLLTable(LLTable)  # 打印预测分析表# 存储分析栈LLStack = "#" + start_symbol# 存储剩余输入串input_tokens = ch.split()  # 使用空格分隔字符串,并得到token列表# 存储剩余输入串strings = input_tokensprint(" ".join(strings).replace("#",""))# 反向入栈for token in reversed(input_tokens):inputs.push(token)sem.push('#')sem.push(start_symbol)init()print("-------------------------------------------------")print("|{:16} {:20} {}|".format('分析栈', '剩余串', '产生式'))LL_driver()

file.txt

# 以井号开头的行为注释
PROGRAM -> STATEMENT PROGRAM | ε
STATEMENT -> VARIABLEDECLARATION | EXECUTIONSTATEMENT
VARIABLEDECLARATION -> TYPE DECLARATIONLIST ;
TYPE -> int | char | float
DECLARATIONLIST -> IDENTIFIER INITIALIZATION MOREIDENTIFIER
IDENTIFIER -> identifier
INITIALIZATION -> ε | = FUZHI
FUZHI -> IDENTIFIER | NUMBER
NUMBER -> intconstant
MOREIDENTIFIER -> ε | , IDENTIFIER INITIALIZATION MOREIDENTIFIEREXECUTIONSTATEMENT -> EXPRESSIONSTATEMENT | IFSTATEMENT | WHILESTATEMENT
EXPRESSIONSTATEMENT -> IDENTIFIER = EXPRESSION ;
EXPRESSION ->  ITEM EXPRESSIONBACK
ITEM -> YINZI XIANGWEI
YINZI -> IDENTIFIER | NUMBER | ( EXPRESSION )
XIANGWEI -> * YINZI XIANGWEI | / YINZI XIANGWEI | ε
EXPRESSIONBACK -> + YINZI EXPRESSIONBACK | - YINZI EXPRESSIONBACK | < YINZI EXPRESSIONBACK | > YINZI EXPRESSIONBACK | ε
IFSTATEMENT -> if ( EXPRESSION ) CODEBLOCK IFSTATEMENTTAIL
IFSTATEMENTTAIL -> else { PROGRAM } | ε
WHILESTATEMENT ->  while ( EXPRESSION ) CODEBLOCK
CODEBLOCK -> { PROGRAM }

source_code.txt

char c ;
float ft , ff = 1 ;
int i , a = 1 , b = 3 ;
char ch = c ;
a = ( a - b ) + 10 ;if ( b * b > 3 ) {b = a - b - 10 ;
} else {b = b * 3 ;
}while ( a > 1 ) {a = a / 10 ;
}#

运行

文法注释

# PROGRAM 表示整个程序
PROGRAM -> STATEMENT PROGRAM | ε# STATEMENT 表示语句
STATEMENT -> VARIABLEDECLARATION | EXECUTIONSTATEMENT# VARIABLEDECLARATION 表示变量声明语句
VARIABLEDECLARATION -> TYPE DECLARATIONLIST ;# TYPE 表示变量的数据类型,可以是 int、char、float
TYPE -> int | char | float# DECLARATIONLIST 表示变量声明的标识符列表
DECLARATIONLIST -> IDENTIFIER INITIALIZATION MOREIDENTIFIER# IDENTIFIER 表示变量的标识符
IDENTIFIER -> variableidentifier# INITIALIZATION 表示变量初始化部分,可以为空或包含赋值
INITIALIZATION -> ε | = FUZHI# FUZHI 表示赋值操作,可以是标识符或数字
FUZHI -> IDENTIFIER | NUMBER# NUMBER 表示整数常量
NUMBER -> intconstant# MOREIDENTIFIER 表示更多的标识符,可以为空或包含逗号、标识符和初始化
MOREIDENTIFIER -> ε | , IDENTIFIER INITIALIZATION MOREIDENTIFIER# EXECUTIONSTATEMENT 表示执行语句,可以是表达式语句、条件语句或循环语句
EXECUTIONSTATEMENT -> EXPRESSIONSTATEMENT | IFSTATEMENT | WHILESTATEMENT# EXPRESSIONSTATEMENT 表示表达式语句,通常包含标识符、等号和表达式
EXPRESSIONSTATEMENT -> IDENTIFIER = EXPRESSION ;# EXPRESSION 表示数学表达式,由项和表达式尾组成
EXPRESSION -> ITEM EXPRESSIONBACK# ITEM 表示表达式中的项,由因子和项尾组成
ITEM -> YINZI XIANGWEI# YINZI 表示因子,可以是标识符、数字或用括号括起来的表达式
YINZI -> IDENTIFIER | NUMBER | ( EXPRESSION )# XIANGWEI 表示项尾,可以为空或包含乘法、除法和因子
XIANGWEI -> * YINZI XIANGWEI | / YINZI XIANGWEI | ε# EXPRESSIONBACK 表示表达式尾,可以为空或包含加法、减法、小于、大于等关系运算符和因子
EXPRESSIONBACK -> + YINZI EXPRESSIONBACK | - YINZI EXPRESSIONBACK | < YINZI EXPRESSIONBACK | > YINZI EXPRESSIONBACK | ε# IFSTATEMENT 表示条件语句,包含 if 关键字、条件表达式、代码块和可能的 else 部分
IFSTATEMENT -> if ( EXPRESSION ) CODEBLOCK IFSTATEMENTTAIL# IFSTATEMENTTAIL 表示条件语句的尾部,可以为空或包含 else 关键字和代码块
IFSTATEMENTTAIL -> else { PROGRAM } | ε# WHILESTATEMENT 表示循环语句,包含 while 关键字、条件表达式和代码块
WHILESTATEMENT -> while ( EXPRESSION ) CODEBLOCK# CODEBLOCK 表示代码块,包含大括号中的语句序列
CODEBLOCK -> { PROGRAM }

file.txt是文法,source_code.txt是待分析字符串

运行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/225341.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android studio 花式按键

一、activity_main.xml代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.a…

【C++】STL 容器 - list 双向链表容器 ① ( 容器特点 | 容器操作时间复杂度 | 构造函数 )

文章目录 一、 list 双向链表容器简介1、容器特点2、容器操作时间复杂度3、遍历访问5、头文件 二、 list 双向链表容器 构造函数1、默认无参构造函数2、创建包含 n 个相同元素的 list 双向链表3、使用初始化列表构造 list 双向链表4、使用另外一个 list 容器 构造 list 双向链表…

【Java基础】Java中异常分类,他们之间的区别?

&#x1f341;Java中异常分哪两类 &#x1f341;Java中异常类&#x1f341;受检异常&#x1f341;非受检异常 &#x1f341;拓展知识仓&#x1f341;什么是Throwable&#x1f341;Error和Exception的区别和联系&#x1f341; 列举几个常用的RuntimeException&#x1f341;Java异…

关于“Python”的核心知识点整理大全41

目录 scoreboard.py game_functions.py game_functions.py 14.3.8 显示等级 game_stats.py scoreboard.py scoreboard.py scoreboard.py game_functions.py game_functions.py alien_invasion.py 14.3.9 显示余下的飞船数 ship.py scoreboard.py 我们将最高得分圆整…

微信这样分类客户,帮你轻松提升业绩!

无论是什么行业&#xff0c;都会遇到各种各样的客户&#xff0c;能不能成交这些客户&#xff0c;关键的一点在于有没有明确的客户分类。 今天就给大家分享几个高效分类客户的方法&#xff0c;帮助大家提高成交率和业绩。 1、分组管理 在微信中创建不同的分组&#xff0c;比如…

MyBatis——MyBatis的缓存

MyBatis的缓存 创建工程&#xff1a; 1缓存介绍 为什么使用缓存&#xff1f; 首次访问时&#xff0c;查询数据库&#xff0c;并将数据存储到内存中&#xff1b;再次访问时直接访问缓存&#xff0c;减少IO、硬盘读写次数、提高效率 Mybatis中的一级缓存和二级缓存&#xff1f;…

IP 地址归属地查询

IP 地址归属地查询 1. IP 地址归属地查询2. IP 地址归属地查询References 1. IP 地址归属地查询 https://tool.lu/ip/index.html 2. IP 地址归属地查询 https://www.ip.cn/ip/.html References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

【JavaScript】Set、Map、WeakSet、WeakMap

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…

Gin框架之使用 go-ini 加载.ini 配置文件

首先,联想一个问题,我们在部署服务时,通常为了方便,对于需要迭代更新的代码进行修改,但是比对shell,可以搞一个变量将需要修改的,以及修改起来变动处多的,写在变量内,到时候如果需要变更,可以直接变更变量即可; 那么,golang有没有什么方式可以将需要变的东西保存起…

TCP协议及工作原理(三)客户端的搭建

ui界面的搭建 &#xff1a; QTcpServer是基于TCP的服务器类提供一种方便的方式管理和创建TCP服务器&#xff0c;QTcpSocket处理TCP套接字编程用于建立TCP连接&#xff0c;发送接收数据等功能。 参考前两篇可深入理解&#xff01;&#xff01;&#xff01;&#xff01;&#xff…

php 不加后缀访问

实现不带后缀访问php文件的方法&#xff1a;首先在htaccess文件中加入内容“RewriteRule ^(api/token) token.php [L]”&#xff1b;然后通过根目录下的“token.php”来接受“api/token”&#xff1b;最后修改配置文件。 考虑的做法有&#xff1a; HTTP重写技术&#xff0c;让…

Win10电脑蓝牙默认音量100的设置教程

在Win10电脑操作过程中&#xff0c;用户想设置连接蓝牙后音量默认是100&#xff0c;但不知道具体的设置操作步骤。这时候用户需要打开Win10系统上的注册表&#xff0c;点击修改注册表来完成这一设置&#xff0c;下面就是Win10电脑蓝牙默认音量100的设置教程介绍&#xff0c;帮助…

【Java、Python】获取电脑当前网络IP进行位置获取(附源码)

我相信看到这篇博客的时候心里肯定是想解决自己的一个问题的&#xff0c;而这篇博客我就以简单快速的方式解决这些烦恼&#xff01; 一、获取当前IP 在Java中自带了一些自己的流对象来获取当前的IP地址&#xff0c;不多说我们直接上代码。 //获取当前网络ip地址 ipAddress Ine…

CentOS安装MongoDB

CentOS安装MongoDB 文章目录 CentOS安装MongoDB1. 安装并运行2. 创建用户/密码3. 测试语句4. 允许外网访问 1. 安装并运行 在 CentOS 上安装 MongoDB&#xff0c;你可以按照以下步骤进行&#xff1a; 导入 MongoDB 的 GPG 密钥&#xff1a; sudo rpm --import https://www.mon…

React快速入门之组件

组件 文件&#xff1a;Profile.js export default function Profile({isPacked true&#xff0c;head,stlyeTmp,src,size 80}) {if (isPacked) {head head " ✔";}return (<div><h1>{head}</h1><imgsrc{src}alt"Katherine Johnson&q…

MongoDB ReplicaSet 部署

文章目录 前言1. 环境准备2. 生成密钥3. 配置参数4. 创建 ReplicaSet5. 副本集维护5.1 新增成员5.2 移除节点5.4 主节点降级5.5 阻止选举5.6 允许副本节点读5.7 延迟观测 6. 连接副本集 后记 前言 本篇文章介绍 MongoDB ReplicaSet 如何搭建&#xff0c;及常用的维护方法。 1…

Redis-实践知识

转自极客时间Redis 亚风 原文视频&#xff1a;https://u.geekbang.org/lesson/535?article681062 Redis最佳实践 普通KEY Redis 的key虽然可以自定义&#xff0c;但是最好遵循下面几个实践的约定&#xff1a; 格式&#xff1a;[业务名称]:[数据名]:[id] 长度不超过44字节 不…

【Jmeter】Jmeter基础9-BeanShell介绍

3、BeanShell BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法。 3.1、Jmeter中使用的BeanShell 在Jmeter中&#xff0c;除了配置元件&#xff0c;其他类型的元件中都有BeanShell。BeanShell 是一种完全符合Java语法规范的脚本语言,并且又拥…

TCP并发服务器

一.进程实现TCP并发服务器 #include <func.h> #define PORT 6666 #define IP "192.168.124.42"void handler(int arm) {while(waitpid(-1,NULL,WNOHANG) > 0); } int main(int argc, const char *argv[]) {//接受17号信号signal(17, handler);i…

Matlab之State Flow

打开方式 方式一&#xff1a;在命令窗口输入State Flow或者简写sf就能打开&#xff0c;并且会自动打开State Flow 的Library。从左到右分别是图表、真值表、状态转换表、例子、顺序查看&#xff0c;可以加入到Simulink当中。 方式二&#xff1a;从Simulink Library里面添加Sta…