-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cpp
More file actions
498 lines (433 loc) · 17.3 KB
/
Copy pathProgram.cpp
File metadata and controls
498 lines (433 loc) · 17.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
#include "Program.h"
#include <cctype>
#include <sstream>
#include <stdexcept>
#include <QString>
#include "ExpressionParser.h"
// ========================
// Program.cpp
// 负责 BASIC 源码的存储、解析、语法树文本生成等核心逻辑。
// ========================
Program::Program(QObject *parent)
: QObject(parent)
{
// pass
// 为了让 Program 继承 QObject
}
// 添加或更新一行源码。
// @param line 行号
// @param code 该行的 BASIC 代码
// 若 code 为空则删除该行,否则解析为 Statement 并存储。
void Program::addSource(int line, const std::string& code) {
std::string trimmed = trim(code); // trim 去除字符串首尾空白字符。
if (trimmed.empty()) {
m_lines.erase(line);
return;
}
// 在插入 map 之前先解析,确保语法错误立即暴露。
auto statement = parseStatement(trimmed);
LineEntry entry;
entry.raw = trimmed;
entry.statement = std::move(statement);
// move作用是转移智能指针所有权
m_lines[line] = std::move(entry);
// 把这个 LineEntry 存到 m_lines 里
// 如果该行号已存在,则覆盖原有内容;如果不存在,则新增一行
}
// 删除指定行。
void Program::removeSource(int line) {
m_lines.erase(line);
}
// 清空所有源码。
void Program::clear() {
m_lines.clear();
}
// 获取所有源码文本,按行号排序。
std::string Program::getAllSource() const {
std::ostringstream oss;
for (const auto& [line, entry] : m_lines) {
oss << line << " " << entry.raw << "\n";
}
return oss.str();
}
// 获取所有语句的语法树文本,供 GUI 展示。
std::string Program::getSyntaxTreeText(bool isRun) const {
std::ostringstream oss;
for (const auto& [line, entry] : m_lines) {
if (!entry.statement) continue;
if(isRun)
oss << line << " " << entry.statement->headerWithStats() << "\n";
else
oss << line << " " << entry.statement->header() << "\n";
// 不是run状态树,那么uesMap设为nullptr
// bodyToTreeString 就不会处理LET变量使用次数
const auto *useMap = isRun ? &varUseCount : nullptr;
std::string body = entry.statement->bodyToTreeString(4, useMap);
if (!body.empty()) {
oss << body;
}
}
return oss.str();
}
void Program::run() {
// 清空计数
if (!m_waitingInput) {
for (auto &pair : m_lines) {
if (pair.second.statement) {
pair.second.statement->resetRuntimeStats();
}
}
}
m_state.clear();
run(m_state);
}
void Program::run(EvalState &state) {
if (m_waitingInput) {
// 再次调用 run() 可能来自 UI 的按钮或递归调用:
// 如果 m_waitingInput 已经被置位,说明解释器正等待用户输入,
// 此时不能继续执行,只需悄悄返回等待 resumeInput。
return;
}
if (!m_resumeAfterInput) {
// 只有在真正的新一轮 RUN 时才清空 use 统计
varUseCount.clear();
}
auto iter = m_lines.begin();
if (m_resumeAfterInput) {
// resumeInput 在成功写回变量时会把 m_resumeAfterInput 设为 true,
// 并把 m_resumeLine 记成“刚刚触发 INPUT 的那一行”。
// 这里需要把迭代器挪到那一行,再 ++iter 从下一行继续执行,
m_resumeAfterInput = false;
if (m_resumeLine >= 0) {
auto resumeIter = m_lines.find(m_resumeLine);
if (resumeIter != m_lines.end()) {
iter = resumeIter;
++iter;
} else {
iter = m_lines.end();
}
}
m_resumeLine = -1;
}
while (iter != m_lines.end()) {
auto *statement = iter->second.statement.get();
if (!statement) {
++iter;
continue;
}
try {
statement->execute(state);
// 统计变量使用次数,下同
statement->collectVarUses(varUseCount);
// emit statementExecuted();
++iter;
// 尝试执行,通过后续catch到的东西进一步处理
// 基于异常的控制流
} catch (const JumpSignal &jump) {
statement->collectVarUses(varUseCount);
// jump指令
auto target = m_lines.find(jump.targetLine());
if (target == m_lines.end()) {
throw std::runtime_error("Jump to undefined line: " + std::to_string(jump.targetLine()));
}
iter = target;
} catch (const EndSignal &) {
statement->collectVarUses(varUseCount);
// END
break;
} catch (const PrintSignal& print) {
statement->collectVarUses(varUseCount);
// PRINT
// 在 Program::run 捕获 PrintSignal 时 emit
emit printRequested(QString::fromStdString(print.text()));
// 连接到ui的textBrowser append
++iter;
} catch (const InputSignal &input) {
statement->collectVarUses(varUseCount);
// INPUT:语句内部通过抛 InputSignal 告诉 Program 需要一个用户输入。
// run() 捕获后做几件事:
// 1) 记录当前行号、变量名,方便 resume 时继续;
// 2) 把等待状态标记为 true,阻止后续误触发 run;
// 3) emit inputRequested(...) 让 UI 切换到输入模式;
// 4) 立即 return,等待 UI 收到信号后主动调用 resumeInput。
m_waitingInput = true; // 等待状态 true
m_pendingLine = iter->first; // 记录行号
m_pendingInputVar = QString::fromStdString(input.variable());
// UI 显示用的变量名,QString
m_pendingInputVarName = input.variable();
// EvalState 写回用的变量名,std::string
m_resumeLine = m_pendingLine; // 当前行号
emit inputRequested(m_pendingInputVar);
// 传UI变量名是为了方便提示显示,具体赋值在on_editline那个函数里面
return; // ret,input处理完后,从记录行号进入run函数
} catch (const std::exception &ex) {
throw std::runtime_error(
"Runtime error at line " + std::to_string(iter->first) + ": " + ex.what());
}
}
}
void Program::resumeInput(const QString &text) {
if (!m_waitingInput) {
// 若没有挂起输入却误调用 resumeInput,直接忽略即可。
return;
}
// 1) UI 提供的原始字符串需要去除空白并验证是否为整数;
// 若 toInt 转换失败,抛异常交由 UI 弹窗提示,并保持在输入模式。
QString trimmed = text.trimmed();
bool ok = false;
int value = trimmed.toInt(&ok);
if (!ok) {
throw std::runtime_error("INVALID NUMBER");
}
if (m_pendingInputVarName.empty()) {
throw std::runtime_error("INPUT state corrupted");
}
// 2) 输入合法,写回 EvalState 中对应的变量。
// 3) 清空挂起状态,并把 resumeAfterInput 置 true,
// 让下一次 run() 知道需要从 INPUT 所在行的下一行继续执行。
m_state.setValue(m_pendingInputVarName, value);
m_waitingInput = false;
m_resumeAfterInput = true;
// 调用run后,会进入恢复逻辑,跳转之前行号++,再接着执行
m_resumeLine = m_pendingLine;
m_pendingLine = -1;
m_pendingInputVar.clear();
m_pendingInputVarName.clear();
run(m_state);
}
// 去除字符串首尾空白字符。
std::string Program::trim(const std::string& text) {
const char* whitespace = " \t\n\r\f\v";
auto start = text.find_first_not_of(whitespace);
if (start == std::string::npos) return "";
auto end = text.find_last_not_of(whitespace);
return text.substr(start, end - start + 1);
}
// 转为大写,便于关键字匹配。
std::string Program::toUpper(const std::string& text) {
std::string result = text;
for (char& ch : result) {
ch = static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
}
return result;
}
// 判断字符串是否以 keyword 开头,且后面为结尾或空白。
bool Program::keywordMatch(const std::string& upper, const std::string& keyword) {
if (upper.size() < keyword.size()) return false;
if (upper.compare(0, keyword.size(), keyword) != 0) return false;
// 检查 upper 的前 keyword.size() 个字符是否和 keyword 完全一致
if (upper.size() == keyword.size()) return true;
// 字符一致,长度一致,比如单纯 END
char next = upper[keyword.size()];
return std::isspace(static_cast<unsigned char>(next));
// 判断关键字后面第一个字符是不是空格,比如 PRINT 100
}
bool Program::isValidVariableName(const std::string &variable) const
{
if (variable.empty()) return false;
// 首字符必须是字母或下划线
if (!std::isalpha(variable[0]) && variable[0] != '_') return false;
// 其余字符只能是字母、数字或下划线
for (size_t i = 1; i < variable.size(); ++i) {
if (!std::isalnum(variable[i]) && variable[i] != '_') return false;
}
// 不能是关键字
static const std::vector<std::string> keywords = {
"IF", "LET", "PRINT", "INPUT", "GOTO", "END", "REM", "THEN"
};
std::string upper = toUpper(variable);
for (const auto &kw : keywords) {
if (upper == kw) return false;
}
return true;
}
bool Program::canExportToY86(std::string &reason) const
{
reason.clear();
if (m_lines.empty()) {
reason = "程序为空,无法导出 Y86";
return false;
}
bool hasLet = false;
for (const auto &pair : m_lines) {
const Statement *stmt = pair.second.statement.get(); // stmt : statement
if (!stmt) {
reason = "第 " + std::to_string(pair.first) + " 不支持导出";
return false;
}
if (dynamic_cast<const LetStatement*>(stmt)) {
hasLet = true;
continue;
}
if (dynamic_cast<const RemStatement*>(stmt)) {
continue;
}
if (dynamic_cast<const EndStatement*>(stmt)) {
continue;
}
reason = "第 " + std::to_string(pair.first) + " 行的语句 '" + stmt->header() + "' 暂不支持导出";
return false;
}
if (!hasLet) {
reason = "程序中没有 LET 语句,无法导出 Y86";
return false;
}
return true;
}
std::vector<Program::LineSnapshot> Program::getLineSnapshots() const
{
std::vector<LineSnapshot> snapshots;
snapshots.reserve(m_lines.size());
for (const auto &pair : m_lines) {
LineSnapshot snap;
snap.lineNumber = pair.first;
snap.statement = pair.second.statement.get();
snap.rawSource = pair.second.raw;
snapshots.push_back(std::move(snap));
}
return snapshots;
}
// 解析一行源码为 Statement 对象。
// 支持 REM/LET/PRINT/INPUT/GOTO/IF/END 及省略 LET 的赋值。
// @throws std::runtime_error 语法错误
std::unique_ptr<Statement> Program::parseStatement(const std::string& code) const {
std::string upper = toUpper(code);
//转为大写,便于关键字匹配。
if (keywordMatch(upper, "REM")) {
std::string comment = trim(code.substr(3));
return std::make_unique<RemStatement>(comment);
// 创建一个 RemStatement 对象,返回给调用者
// 返回指向 RemStatement 对象的智能指针
}
if (keywordMatch(upper, "LET")) {
std::string rest = trim(code.substr(3));
return parseLet(rest);
}
if (keywordMatch(upper, "PRINT")) {
std::string rest = trim(code.substr(5));
return parsePrint(rest);
}
if (keywordMatch(upper, "INPUT")) {
std::string rest = trim(code.substr(5));
return parseInput(rest);
}
if (keywordMatch(upper, "GOTO")) {
std::string rest = trim(code.substr(4));
return parseGoto(rest);
}
if (keywordMatch(upper, "IF")) {
std::string rest = trim(code.substr(2));
return parseIf(rest);
}
if (keywordMatch(upper, "END")) {
return std::make_unique<EndStatement>();
}
// 支持省略 LET 关键字的赋值写法,例如 "A = 1"。
auto eqPos = code.find('=');
if (eqPos != std::string::npos) {
return parseLet(code);
}
throw std::runtime_error("Unrecognized statement: " + code);
}
// 解析 LET 语句或赋值语句。
// @param rest 形如 "A = 1" 或 "A=1"
// @throws std::runtime_error 语法错误
std::unique_ptr<Statement> Program::parseLet(const std::string& rest) const {
auto eqPos = rest.find('=');
if (eqPos == std::string::npos) {
throw std::runtime_error("LET statement missing '='");
}
std::string variable = trim(rest.substr(0, eqPos));
// 取等号左边的内容,去掉空白,得到变量名
// 变量名合法性
if (variable.empty()) {
throw std::runtime_error("LET statement missing variable name");
} else if (!isValidVariableName(variable)) {
throw std::runtime_error("invalid variable name");
}
std::string exprText = trim(rest.substr(eqPos + 1));
// 取等号右边的内容,去掉空白,得到表达式文本
if (exprText.empty()) {
throw std::runtime_error("LET statement missing expression");
}
ExpressionParser parser(exprText);
auto expr = parser.parse();
// 用 ExpressionParser 解析表达式文本,得到表达式语法树(expr)
// return std::make_unique<LetStatement>(toUpper(variable), std::move(expr));
return std::make_unique<LetStatement>(variable, std::move(expr));
// 变量名需要区分大小写,不能统一toUpper处理
}
// 解析 PRINT 语句。
// @param rest 表达式部分
// @throws std::runtime_error 语法错误
std::unique_ptr<Statement> Program::parsePrint(const std::string& rest) const {
if (rest.empty()) {
throw std::runtime_error("PRINT statement missing expression");
}
ExpressionParser parser(rest);
auto expr = parser.parse();
return std::make_unique<PrintStatement>(std::move(expr));
}
// 解析 INPUT 语句。
// @param rest 变量名部分
// @throws std::runtime_error 语法错误
std::unique_ptr<Statement> Program::parseInput(const std::string& rest) const {
std::istringstream iss(rest);
// 把 rest(INPUT 后面的内容)当作流来处理
std::string name;
iss >> name;
// 读取第一个单词到 name 变量里(自动跳过前面的空格)
// 变量名合法性
if (name.empty()) {
throw std::runtime_error("INPUT statement missing variable name");
} else if (!isValidVariableName(name)) {
throw std::runtime_error("invalid variable name");
}
return std::make_unique<InputStatement>(name);
// 变量名需要区分大小写,不能统一toUpper处理
}
// 解析 GOTO 语句。
// @param rest 行号部分
// @throws std::runtime_error 语法错误
std::unique_ptr<Statement> Program::parseGoto(const std::string& rest) const {
std::string target = trim(rest);
if (target.empty()) {
throw std::runtime_error("GOTO statement missing target line");
}
int line = std::stoi(target);
// stoi 把字符串转换为 int 整数
return std::make_unique<GotoStatement>(line);
}
// 解析 IF 语句。
// @param rest 形如 "a > b THEN 100"
// @throws std::runtime_error 语法错误
std::unique_ptr<Statement> Program::parseIf(const std::string& rest) const {
std::string upper = toUpper(rest);
auto thenPos = upper.find("THEN");
if (thenPos == std::string::npos) {
throw std::runtime_error("IF statement missing THEN");
}
std::string condition = trim(rest.substr(0, thenPos));
std::string targetText = trim(rest.substr(thenPos + 4));
// 取 "THEN" 前面的内容为条件表达式,后面的内容为目标行号
if (condition.empty() || targetText.empty()) {
throw std::runtime_error("Invalid IF statement");
}
// 条件部分只允许出现一次比较符号(=、<、>)。
std::size_t opPos = condition.find_first_of("=<>");
if (opPos == std::string::npos) {
throw std::runtime_error("IF condition missing comparison operator");
}
std::string op(1, condition[opPos]);
std::string leftText = trim(condition.substr(0, opPos));
std::string rightText = trim(condition.substr(opPos + 1));
if (leftText.empty() || rightText.empty()) {
throw std::runtime_error("Invalid IF condition");
}
ExpressionParser leftParser(leftText);
ExpressionParser rightParser(rightText);
auto leftExpr = leftParser.parse();
auto rightExpr = rightParser.parse();
int targetLine = std::stoi(targetText);
return std::make_unique<IfStatement>(std::move(leftExpr), op, std::move(rightExpr), targetLine);
}