(1)理解有穷自动机及其应用。
(2)掌握 NFA 到 DFA 的等价变换方法、DFA 最小化的方法。
(3)掌握设计、编码、调试词法分析程序的技术和方法。
编写一个 PL/0 词法分析程序,使用java语言。
1)算法描述:
2)程序结构
3)主要变量说明:
4)程序
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class LexicalAnalyzer1 {
public static void main(String[] args) {
String filePath = "C:\\Users\\dell\\Desktop\\input.txt"; // 请将 "YourUserName" 替换为您的计算机用户名
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
int lineNumber = 1;
while ((line = reader.readLine()) != null) {
analyzeLine(line, lineNumber);
lineNumber++;
}
}
catch (IOException e) {
System.out.println("文件读取失败");
e.printStackTrace();
}
}
private static void analyzeLine(String line, int lineNumber) {
String[] tokens = line.split("\\s+");
for (String token : tokens) {
if (token.isEmpty()) {
continue;
}
if (isKeyword(token)) {
System.out.println("关键字: " + token);
}
else if (isIdentifier(token)) {
System.out.println("标识符: " + token);
}
else if (isNumber(token)) {
System.out.println("常数: " + token);
}
else {
System.out.println("错误词法: " + token + " (第 " + lineNumber + " 行)");
}
}
}
private static boolean isKeyword(String token) {
String[] keywords = { "const", "var", "main","procedure", "begin", "end", "if", "then", "else", "while", "do", "call", "read", "write", "int", "char", "bool", "float", "double", "long", "short", "unsigned", "signed", "void", "return" };
for (String keyword : keywords) {
if (keyword.equals(token)) {
return true;
}
}
return false;
}
private static boolean isIdentifier(String token) {
return token.matches("[a-zA-Z{}\\(\\)=;][a-zA-Z0-9{}\\(\\)=;]*");
}
private static boolean isNumber(String token) {
return token.matches("[0-9]+");
}
}
能够正常运行
6)程序截图
通过编写这段词法分析器的代码,我加深了对词法分析的理解,掌握了字符串处理和正则表达式的应用技巧。
清华大学课本LR(0)文法知识
csdn博主杨戬的博客
(1)熟练掌握 LL(1)分析表的构造方法。
(2)掌握设计、编制和调试典型的语法分析程序,进一步掌握常用的语法分析方法
根据 LL(1)分析法自己编写一个语法分析程序,使用c++语言。
1)算法描述:实现了一个基于LL(1)文法的语法分析器。它通过输入一个字符串,使用LL(1)文法规则对字符串进行分析,判断输入的字符串是否符合给定文法的规则。
2)程序结构:
在主函数中,首先输出提示信息,然后读取输入的字符串。
调用analyze函数进行语法分析,传入输入的字符串和字符串长度。在analyze函数中,使用循环进行语法分析的过程:初始化计数器cnt和分析位置i,以及栈cmp。定义一个字符数组p,用于存储分析过程中的产生式。输出分析过程的表头信息。在循环中,首先获取栈顶符号ch。如果ch是非终结符,则根据LL1文法规则查找产生式,并进行相应的处理。
如果分析过程正常结束,即字符串被接受,输出接受信息。
3)主要变量说明
LL1:二维字符串数组,存储了LL(1)文法的产生式规则。
H:字符串常量,表示非终结符的集合。
L:字符串常量,表示终结符的集合。
cmp:栈,用于存储分析过程中的符号。
str:字符数组,存储输入的字符串。
len:整数,表示输入字符串的长度。
p:字符数组,用于存储分析过程中的产生式。
pindex:整数,表示字符数组p的索引位置。
4)程序内容:
#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
const string LL1[5][6] = {
{ "->AT", "->AT", "null", "null", "null", "null"},
{ "->BU", "->BU", "null", "null", "null", "null"},
{ "null", "null", "->$", "->+AT", "null", "->$"},
{ "null", "null", "->$", "->$", "->*BU", "->$"},
{ "->m", "->(S)", "null", "null", "null", "null"}
};
const string H = "SATUB";
const string L = "m()+*#";
stack<char> cmp;
int findH(char a) {
size_t pos = H.find(a);
if (pos != string::npos) {
return static_cast<int>(pos);
}
return -1;
}
int findL(char a) {
size_t pos = L.find(a);
if (pos != string::npos) {
return static_cast<int>(pos);
}
return -1;
}
int error(int i, int cnt, int len, const char p[], const char str[]) {
cout << cnt << '\t' << p << '\t';
for (int q = i; q < len; q++) {
cout << str[q];
}
cout << "\t报错" << endl;
return len;
}
void analyze(const char str[], int len) {
int cnt = 1;
int i = 0;
stack<char> cmp;
cmp.push('#');
cmp.push('S');
char p[2000] = "#S";
int pindex = 2;
cout << "Step\tStack\tString\tRule" << endl;
while (i < len) {
char ch = cmp.top();
if (ch >= 'A' && ch <= 'Z') {
cmp.pop();
int x = findH(ch);
int y = findL(str[i]);
if (x != -1 && y != -1) {
string rule = LL1[x][y];
if (rule == "null") {
i = error(i, cnt, len, p, str);
continue;
}
cout << cnt << '\t' << p << '\t';
if (p[pindex - 1] != '#') {
p[pindex] = '\0';
pindex--;
}
if (rule[2] != '$') {
for (int q = static_cast<int>(rule.length()) - 1; q > 1; q--) {
p[pindex++] = rule[q];
cmp.push(rule[q]);
}
}
else {
pindex--;
}
for (int q = i; q < len; q++) {
cout << str[q];
}
cout << '\t' << ch << rule << endl;
}
else {
i = error(i, cnt, len, p, str);
continue;
}
}
else {
if (ch == str[i]) {
cmp.pop();
cout << cnt << '\t' << p << '\t';
if (ch == '#' && str[i] == '#') {
cout << "#\t接受" << endl;
return;
}
for (int q = i; q < len; q++) {
cout << str[q];
}
cout << '\t' << ch << "匹配" << endl;
pindex--;
i++;
}
else {
i = error(i, cnt, len, p, str);
continue;
}
}
cnt++;
}
}
int main() {
cout << "请输入一个字符串" << endl;
char str[200];
cin >> str;
int len = strlen(str);
cmp.push('#');
cmp.push('S');
analyze(str, len + 1);
return 0;
}
5)调试情况:
运行一切正常
6)运行截图:
通过分析该代码,我对LL(1)语法分析的原理和实现有了更深入的理解。这样的语法分析器在编译器和解释器等领域有广泛的应用,能够帮助我们验证和分析输入的程序是否符合给定的文法规则,从而提高程序的正确性和可靠性。
清华大学课本LL(1)文法知识
csdn博主杨戬的博客
(1)掌握 LR(0)分析表的构造方法。
(2)掌握设计、编制和调试典型的语法分析程序,进一步掌握常用的语法分析方法。
(3)理解语法分析在编译程序中的作用
自己编写一个基于 LR(0)方法的语法分析程序。语言不限,文法不限。 此时可根据自己的实际情况,选择以下一项实现分析算法中分析表的构造: (1)直接输入根据已知文法构造的 LR(0)分析表;
(2)输入已知文法的项目集规范族和转换函数,由程序自动生成 LR(0)分析表;
(3)输入已知文法,由程序自动生成 LR(0)分析表
1)算法描述
读入一个带有结束符#的输入串。使用LR(0)分析表进行分析,根据当前状态和输入符号进行查表操作,得到相应的动作。如果是移入动作,则将输入符号移入符号栈和输入串,并切换到下一个状态。如果是规约动作,则根据产生式将符号栈中的符号规约为非终结符,并进行状态转换。如果是接受动作,则分析成功,结束分析过程。如果在查表过程中遇到空白或错误情况,则报错。
2)程序结构
1.程序主要包含一个analyze函数和主函数main。
2.analyze函数用于进行LR(0)语法分析,根据输入串和LR(0)分析表进行分析过程,并输出每一步的状态栈、符号栈、输入串、动作和转移状态。
3.主函数main中首先定义了LR(0)分析表LR0、输入符号集合L、产生式头部集合head以及其他辅助变量。
4.然后通过输入字符串,调用analyze函数进行LR(0)语法分析。
3)主要变量说明
4)程序
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
///表格数组 a b c d # E A B
char LR0[50][50][100] = {
{"S2", "S3", "", "", "", "1", "", ""},
{"", "", "", "", "acc", "", "", ""},
{"", "", "S4", "S10", "", "", "6", ""},
{"", "", "S5", "S11", "", "", "", "7"},
{"", "", "S4", "S10", "", "", "8", ""},
{"", "", "S5", "S11", "", "", "", "9"},
{"r1", "r1", "r1", "r1", "r1", "", "", ""},
{"r2", "r2", "r2", "r2", "r2", "", "", ""},
{"r3", "r3", "r3", "r3", "r3", "", "", ""},
{"r5", "r5", "r5", "r5", "r5", "", "", ""},
{"r4", "r4", "r4", "r4", "r4", "", "", ""},
{"r6", "r6", "r6", "r6", "r6", "", "", ""}
};
char L[200] = "abcd#EAB", head[20] = { 'S', 'E', 'E', 'A', 'A', 'B', 'B' }; ///列判断依据
int del[10] = { 0, 2, 2, 2, 1, 2, 1 }; //0-6号文法每个文法长
int cnt = 1, LR = 0, cindex = 0, sindex = 0;
char cod[300] = "0", sti[300] = "#"; ///初始符号栈对应输出数组
stack<int> now; ///状态栈
stack<char> cmp; ///符号栈
void error(int x, int y) ///报错输出
{
cout << "error:第" << x << "行" << L[y] << "列为空!" << endl;
}
int find(char a) ///对应列寻找
{
int i = 0;
while(i<=7){
if (a == L[i])
{
return i;
}
i++;
}
return -1;
}
int cal(int l, char s[])
{
int num = 0,i=1;
while (i < l) {
num = num * 10 + (s[i] - '0');
i++;
}
return num;
}
void analyze(char str[], int len) ///分析主体过程
{
cout<<"步骤 状态栈 符号栈 输入串 ACTION GOTO"<<endl;
for(;LR <= len;)
{
printf("(%d) %-10s%-10s", cnt, cod, sti); ///步骤,状态栈,符号栈输出
cnt++;
for (int i = LR; i < len; i++) ///输入串输出
{
cout<<str[i];
}
for (int i = len - LR; i < 10; i++)
cout<<" ";
int x = now.top(); ///状态栈栈顶
int y = find(str[LR]); ///待判断串串首
if (strcmp(LR0[x][y], "null") != 0)
{
int l = strlen(LR0[x][y]); ///当前Ri或Si的长度
if (LR0[x][y][0] == 'S') ///Si
{
int t = cal(l, LR0[x][y]); ///整数
printf("%-10s \n", LR0[x][y]); ///ACTION与GOTO
now.push(t);
sindex++;
sti[sindex] = str[LR];
cmp.push(str[LR]);
if(t>=10)
{
int k = 1;
cod[++cindex] = '(';
while (k < l)
{
cod[++cindex] = LR0[x][y][k];
k++;
if (k < l)
{
cod[++cindex] = ')';}
}
}
else
{
cod[++cindex] = LR0[x][y][1];
}
LR++;
}
else if (LR0[x][y][0] == 'r') ///ri,退栈,ACTION和GOTO
{
printf("%-10s", LR0[x][y]);
int t = cal(l, LR0[x][y]);
int g = del[t];
while (g--)
{
now.pop();
cmp.pop();
sti[sindex] = '\0';
sindex--;
}
g = del[t];
while (g > 0)
{
if (cod[cindex] == ')')
{
cod[cindex--] = '\0';
for (;;)
{
cod[cindex--] = '\0';
if (cod[cindex] == '(')
{
break;
}
}
}
else
{
cod[cindex--] = '\0';
}
g--;
}
cmp.push(head[t]);
sindex++;
sti[sindex] = head[t];
x = now.top();
y = find(cmp.top());
t = LR0[x][y][0] - '0';
now.push(t);
cindex++;
cod[cindex] = LR0[x][y][0];
cout << t << endl;
}
else if (LR0[x][y][0] == 'a') ///acc
{
cout << "acc \n"; ///ACTION与GOTO
return;
}
else
{
int t = LR0[x][y][0] - '0';
char ch = ' ';
printf("%-10c%-10d\n", ch, t);
now.push(t);
cod[cindex++] = LR0[x][y][0];
LR++;
}
}
else
{
error(x, y);
return; ///报错
}
}
}
void chart() ///测试表函数
{
printf("-\ta\tb\tc\td\t#\tE\tA\tB\n");
for (int i = 0; i <= 11; i++)
{
printf("%d", i);
for (int j = 0; j <= 8; j++)
printf("\t%s", LR0[i][j]);
cout << endl;
}
cout << endl;
}
int main()
{
chart();
now.push(0);
cmp.push('#');
char str[200]; ///输入串
cout << "请输入字符串(带#):" << endl;
cin >> str;
int len = strlen(str);
analyze(str, len);
system("pause");
return 0;
}
5)调试情况:
一切调试正常
6)运行截图:
编写LR(0)分析器的代码需要对LR(0)分析表和分析过程有深入的理解,并且需要仔细处理产生式、状态栈、符号栈和输入串的操作。通过编写这段代码,我加深了对LR(0)语法分析的理解,并提高了对语法分析算法的实现能力。
清华大学课本LR(0)文法知识
csdn博主杨戬的博客
(1)熟悉语义分析和中间代码生成过程。
(2)加深对语义翻译的理解
在词法分析、语法分析和语义分析程序的基础上,将输入源代码翻译成中间代码。编写一个中间代码生成程序,能将算术表达式等翻译成逆波兰形式.
1)算法描述
2)程序结构
3)主要变量说明
4)程序
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
char ch;
int top, length, t;
char ex[100];
char str[100];
bool isOperator(char c)
{
if (c == '+' || c == '-' || c == '*' || c == '/' || c == '^') {
return true;
}
else {
return false;
}
}
bool isDigit(char c)
{
if (c >= '0' && c <= '9') {
return true;
}
else {
return false;
}
}
double calculate()
{
double stack[100];
int top = -1;
int t = 0;
int length = strlen(ex);
while (t < length)
{
char ch = ex[t];
if (isOperator(ch))
{
if (top < 1)
{
printf("\n\t表达式错误!\n");
return 0.0; // Return 0 for expression error
}
double operand2 = stack[top];
top--;
double operand1 = stack[top];
double result; // Move the declaration here
switch (ch)
{
case '+':
result = operand1 + operand2;
break;
case '-':
result = operand1 - operand2;
break;
case '*':
result = operand1 * operand2;
break;
case '/':
if (operand2 != 0)
result = operand1 / operand2;
else
{
printf("\n\t除零错误!\n");
return 0.0; // Return 0 for division by zero error
}
break;
case '^':
result = 1.0;
for (int i = 0; i < operand2; i++)
{
result *= operand1;
}
break;
default:
printf("\n\t运算符错误!\n");
return 0.0; // Return 0 for invalid operator error
}
top++;
stack[top] = result;
t++;
}
else if (isDigit(ch) || ch == '-')
{
// Remaining code remains the same
}
else
{
t++; // Skip unknown characters
}
}
if (top != 0)
{
printf("\n\t表达式错误!\n");
return 0.0; // Return 0 for expression error
}
return stack[0];
}
int getPrecedence(char c)
{
if (c == '^')
return 3;
if (c == '*' || c == '/')
return 2;
if (c == '+' || c == '-')
return 1;
return 0;
}
void convertToRPN()
{
char stack[100];
top = -1;
t = 0;
for (int i = 0; i < length;)
{
ch = str[i];
if (isDigit(ch))
{
while (ch == '@' || isDigit(ch) || ch == '.')
{
ex[t] = ch;
t++;
i++;
ch = str[i];
}
ex[t] = '&';
t++;
}
else if (isOperator(ch))
{
while (top >= 0 && stack[top] != '(' && getPrecedence(stack[top]) >= getPrecedence(ch))
{
ex[t] = stack[top];
top--;
t++;
}
top++;
stack[top] = ch;
i++;
}
else if (ch == '(')
{
top++;
stack[top] = ch;
i++;
}
else if (ch == ')')
{
while (top >= 0 && stack[top] != '(')
{
ex[t] = stack[top];
top--;
t++;
}
if (top >= 0 && stack[top] == '(')
top--;
i++;
}
else
{
i++;
}
}
while (top >= 0)
{
ex[t] = stack[top];
t++;
top--;
}
ex[t] = '\0';
}
bool isValidExpression()
{
int parenthesesCount = 0;
for (int i = 0; i < length; i++)
{
if (str[i] == '(')
{
parenthesesCount++;
}
else if (str[i] == ')')
{
parenthesesCount--;
if (parenthesesCount < 0)
{
return false;
}
}
}
return (parenthesesCount == 0);
}
int main()
{
char stack[100];
memset(stack, 0, sizeof(stack));
length = 0;
while (true)
{
cin >> ch;
if (ch == '#')
{
break;
}
str[length++] = ch;
}
str[length] = '\0';
printf("原式:%s\n", str);
if (!isValidExpression())
{
printf("语法错误!\n");
return 0;
}
convertToRPN();
printf("逆波兰:%s\n", ex);
length = t;
printf("得出:%f\n", calculate());
return 0;
5)调试情况
调试一切正常
6)运行截图
我对逆波兰表达式的转换和计算有了更深入的理解,同时也加强了对栈这种数据结构的应用。
清华大学课本语义分析知识
csdn博主杨戬的博客
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland