Action数据如何进入值栈
时间: 2024-05-26 21:11:57 浏览: 70
在 Struts2 中,Action 数据可以通过以下几种方式进入值栈:
1. 实现 ModelDriven 接口,将数据模型对象放入值栈中。
2. 在 Action 中定义成员变量,并提供 getter 和 setter 方法,通过属性访问器将数据放入值栈中。
3. 在 Action 中使用 Map 类型的成员变量,将数据放入 Map 对象中,然后将 Map 对象放入值栈中。
4. 在 Action 中使用 Servlet API,通过 request、session、application 等对象将数据放入值栈中。
5. 在 Action 中使用 OGNL 表达式,将数据放入值栈中。
无论通过哪种方式将数据放入值栈,都需要在结果视图中使用 OGNL 表达式将数据从值栈中取出并渲染到页面上。
相关问题
2.ValueStack值栈有什么用?如何在JSP页面访问值栈中root数据?Action数据如何进入值栈?
ValueStack值栈是Struts2框架中的一个重要概念,它用于存储和管理请求处理过程中的数据,可以存放Action的属性和方法,也可以存放当前请求相关的数据和常量。在JSP页面中,可以通过EL表达式访问值栈中的root数据,例如${name}表示获取值栈中root对象中名为name的属性值。Action数据可以通过Struts2框架的interceptor链将Action放入值栈中,即每个拦截器都可以向值栈中添加数据,最终将整个Action对象放入值栈中。
针对文法:G[E]:E->E+T|T T->T*F|F F->(E)|i 用c++编写程序利用LR(0)分析方法对该文法进行语法分析,构建i+i*i句型分析过程中符号栈的变化过程并输出分析表
为了对该文法进行语法分析,我们需要先构建出它的LR(0)自动机,然后根据这个自动机生成语法分析表。我来分别介绍一下这两个步骤。
## 构建LR(0)自动机
首先我们需要定义一些数据结构和辅助函数。这里我用`vector`表示自动机的状态集合,用`map`表示状态转移函数,用`set`表示项目集。
```c++
#include <iostream>
#include <vector>
#include <map>
#include <set>
using namespace std;
// 项目的结构体
struct Item {
int prod; // 产生式的编号
int dot; // 点的位置
set<char> lookahead; // 向前看符号
};
// 产生式的结构体
struct Production {
char left; // 左部符号
string right; // 右部符号串
};
// 判断一个符号是否是终结符
bool isTerminal(char s) {
return islower(s) || s == '(' || s == ')' || s == '+' || s == '*';
}
```
接下来是文法的产生式和项目集。注意,这里我把`'->'`改成了一个冒号`':'`。
```c++
// 文法的产生式
Production prods[] = {
{'E', "E+T"},
{'E', "T"},
{'T', "T*F"},
{'T', "F"},
{'F', "(E)"},
{'F', "i"},
};
int nProds = sizeof(prods) / sizeof(Production);
// 项目集
set<Item> closure[] = {
{{0, 0, {'+'}}, {0, 0, {'*'}}, {0, 0, {')'}}, {0, 0, {'$', '+', '*'}}},
{{0, 1, {'+', '$', '+', '*'}}, {0, 1, {'*', '$', '+', '*'}}},
{{0, 2, {'+', '$', '+', '*'}}, {0, 2, {'*', '$', '+', '*'}}, {0, 2, {')', '$', '+', '*'}}},
{{1, 1, {'+'}}, {1, 1, {'$', '+'}}, {1, 1, {'$', '+', '*'}}},
{{1, 2, {'+', '$', '+'}}, {1, 2, {'*', '$', '+', '*'}}, {1, 2, {')', '$', '+', '*'}}},
{{2, 1, {'+', '*', '$', '+'}}, {2, 1, {'*', '$', '+', '*'}}},
{{2, 2, {'+', '*', '$', '+', '*'}}, {2, 2, {')', '$', '+', '*'}}},
{{3, 1, {'+', '*', '$', '+', '*'}}, {3, 1, {'*', '$', '+', '*'}}, {3, 1, {')', '$', '+', '*'}}},
{{3, 2, {'+', '*', '$', '+', '*'}}, {3, 2, {')', '$', '+', '*'}}},
{{4, 1, {'(', '$', 'i'}}, {4, 1, {'i'}}},
{{5, 1, {'+', '*', '$', '+', '*'}}, {5, 1, {')', '$', '+', '*'}}},
{{5, 2, {'+', '*', '$', '+', '*'}}, {5, 2, {')', '$', '+', '*'}}},
};
int nClosures = sizeof(closure) / sizeof(set<Item>);
```
接下来是主函数。我们先定义一个`vector`表示状态集合,然后把初始状态加入其中。然后对于每个状态,我们遍历其中的每个项目,进行状态转移,然后把新的状态加入到状态集合中。这个过程一直进行到状态集合不再增大为止。
```c++
int main() {
// 初始状态
set<Item> init;
for (auto& item : closure[0]) {
init.insert(item);
}
vector<set<Item>> states{init};
// 对每个状态进行状态转移
map<pair<int, char>, int> gotoTable;
for (int i = 0; i < states.size(); ++i) {
// 遍历项目集中的每个项目
for (auto& item : states[i]) {
// 如果点后面还有符号,进行状态转移
if (item.dot < prods[item.prod].right.size()) {
char next = prods[item.prod].right[item.dot];
set<Item> nextItems;
for (auto& newItem : closure[gotoTable[{i, next}]]) {
nextItems.insert(newItem);
}
for (auto& lookahead : item.lookahead) {
nextItems.insert({item.prod, item.dot + 1, {lookahead}});
}
if (gotoTable.find({i, next}) == gotoTable.end()) {
states.push_back(nextItems);
gotoTable[{i, next}] = states.size() - 1;
}
}
}
}
}
```
这样,我们就构建出了LR(0)自动机。接下来是生成语法分析表。
## 生成语法分析表
语法分析表是一个二维数组,第一维是状态编号,第二维是终结符和非终结符。每个表格中存储的是一个动作,包括移进、规约和接受。移进动作表示将符号压入符号栈并进入下一个状态,规约动作表示将一个产生式规约为一个非终结符并回退到上一个状态,接受动作表示语法分析成功结束。
这里我用`map`表示语法分析表,键是状态和符号的二元组,值是一个动作编号。动作编号是一个正整数,表示移进、规约或接受。
```c++
// 生成语法分析表
map<pair<int, char>, int> actionTable;
map<pair<int, char>, int> gotoTable;
for (int i = 0; i < states.size(); ++i) {
// 处理移进动作
for (auto& item : states[i]) {
if (item.dot < prods[item.prod].right.size()) {
char next = prods[item.prod].right[item.dot];
if (isTerminal(next)) {
int j = gotoTable[{i, next}];
actionTable[{i, next}] = j * 3 + 1;
}
}
}
// 处理规约动作
for (auto& item : states[i]) {
if (item.dot == prods[item.prod].right.size()) {
for (auto& lookahead : item.lookahead) {
actionTable[{i, lookahead}] = item.prod * 3 + 2;
}
}
}
// 处理接受动作
if (i == 0) {
actionTable[{i, '$'}] = 0;
}
// 处理转移动作
for (auto& prod : prods) {
char left = prod.left;
if (!isTerminal(left)) {
int j = gotoTable[{i, left}];
gotoTable[{i, left}] = j;
}
}
}
```
这样,我们就生成了LR(0)语法分析表。接下来是输出符号栈变化过程和语法分析表。
## 符号栈变化过程
在进行语法分析时,我们需要维护一个符号栈。符号栈中存储的是终结符和非终结符的编号。在移进时,我们将当前符号压入符号栈,并转移到下一个状态;在规约时,我们弹出产生式右部长度的符号,并将产生式左部的符号压入符号栈,并回退到上一个状态。
这里我定义了一个`vector`表示符号栈,每次进行移进或规约时都输出符号栈的变化情况。
```c++
// 符号栈
vector<int> stack{0};
for (int i = 0; i < input.size();) {
char symbol = input[i];
int action = actionTable[{stack.back(), symbol}];
if (action % 3 == 1) {
// 移进
int state = action / 3;
stack.push_back(getSymbolId(symbol));
stack.push_back(state);
++i;
cout << setw(10) << left << "shift"
<< setw(10) << left << symbol
<< setw(10) << left << state
<< endl;
} else if (action % 3 == 2) {
// 规约
int prod = action / 3;
auto& right = prods[prod].right;
int n = right.size();
stack.resize(stack.size() - 2 * n);
int symbol = getSymbolId(prods[prod].left);
int state = gotoTable[{stack.back(), prods[prod].left}];
stack.push_back(symbol);
stack.push_back(state);
cout << setw(10) << left << "reduce"
<< setw(10) << left << getSymbolName(symbol)
<< setw(10) << left << prod
<< endl;
} else {
// 接受
cout << setw(10) << left << "accept"
<< endl;
break;
}
}
```
在这个程序中,`getSymbolId`和`getSymbolName`分别是符号的编号和名称之间的转换函数。
```c++
// 获取符号的编号
int getSymbolId(char symbol) {
if (islower(symbol)) {
return symbol - 'a' + 1;
} else if (symbol == '(') {
return 27;
} else if (symbol == ')') {
return 28;
} else if (symbol == '+') {
return 29;
} else if (symbol == '*') {
return 30;
} else if (symbol == '$') {
return 0;
} else {
return symbol - 'A' + 31;
}
}
// 获取符号的名称
string getSymbolName(int symbol) {
if (symbol == 0) {
return "$";
} else if (symbol >= 1 && symbol <= 26) {
return string(1, symbol - 1 + 'a');
} else if (symbol == 27) {
return "(";
} else if (symbol == 28) {
return ")";
} else if (symbol == 29) {
return "+";
} else if (symbol == 30) {
return "*";
} else {
return string(1, symbol - 31 + 'A');
}
}
```
## 语法分析表
最后,我们输出语法分析表。这里我用`setw`控制输出的宽度。
```c++
// 输出语法分析表
cout << setw(7) << left << "state";
for (char symbol : symbols) {
cout << setw(7) << left << symbol;
}
cout << endl;
for (int i = 0; i < states.size(); ++i) {
cout << setw(7) << left << i;
for (char symbol : symbols) {
int action = actionTable[{i, symbol}];
if (action % 3 == 1) {
cout << setw(7) << left << "s" << action / 3;
} else if (action % 3 == 2) {
cout << setw(7) << left << "r" << action / 3;
} else {
cout << setw(7) << left << "acc";
}
}
cout << endl;
for (char symbol : nonterminals) {
int j = gotoTable[{i, symbol}];
cout << setw(7) << left << "" << setw(7) << left << symbol << setw(7) << left << j << endl;
}
}
```
这样,我们就完成了对该文法的LR(0)语法分析程序的编写。完整代码如下:
阅读全文