From d83654837a00ca338d6be5f977a5e013e66d108e Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 15 Sep 2026 17:55:13 +0800 Subject: [PATCH] =?UTF-8?q?c++=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/stage-cpp/cpp-parser.js | 289 ++++++++---------- 1 file changed, 124 insertions(+), 165 deletions(-) diff --git a/scratch-gui/src/components/stage-cpp/cpp-parser.js b/scratch-gui/src/components/stage-cpp/cpp-parser.js index 4d1fbc84..f33304d2 100644 --- a/scratch-gui/src/components/stage-cpp/cpp-parser.js +++ b/scratch-gui/src/components/stage-cpp/cpp-parser.js @@ -2386,202 +2386,162 @@ export class CppCodeExecutor { return position; } - // 字符串表达式 - if (expr.includes('"') || expr.includes("'")) { - return expr.replace(/['"]/g, ''); + // 整个表达式是字符串字面量时才当字符串;含引号的 API 调用走后面的算术解析 + if ((expr.startsWith('"') && expr.endsWith('"')) || + (expr.startsWith("'") && expr.endsWith("'"))) { + return expr.slice(1, -1); } - // 使用改进的表达式解析器处理复杂表达式 return this.parseComplexExpression(expr); } - // 新增:改进的表达式解析器,支持括号嵌套和运算优先级 + // 递归下降:一元 +/- ,然后 * / % 优先于 + - parseComplexExpression(expr) { try { - // 移除所有空格 - expr = expr.replace(/\s+/g, ''); - - // 处理括号 - while (expr.includes('(')) { - const lastOpenParen = expr.lastIndexOf('('); - const closeParen = expr.indexOf(')', lastOpenParen); - - if (closeParen === -1) { - debugLog(`⚠️ 括号不匹配: ${expr}`); - return 0; - } - - const innerExpr = expr.substring(lastOpenParen + 1, closeParen); - const innerResult = this.parseComplexExpression(innerExpr); - - expr = expr.substring(0, lastOpenParen) + innerResult + expr.substring(closeParen + 1); - } - - // 处理一元运算符(负号) - expr = this.handleUnaryOperators(expr); - - // 按正确的运算优先级解析: - // 加法和减法是最低优先级,因此在此处最先切分。 - // 切分后的每一项将交给 parseAdditionSubtraction,在其中处理更高级别的乘除法。 - return this.parseAdditionSubtraction(expr); - + this._exprTokens = this.lexArithmetic(expr); + this._exprPos = 0; + if (this._exprTokens.length === 0) return 0; + const result = this.parseAddExpr(); + return typeof result === 'number' && !Number.isNaN(result) ? result : 0; } catch (error) { debugLog(`⚠️ 表达式解析错误: ${expr}, 错误: ${error.message}`); return 0; } } - // 处理一元运算符 - handleUnaryOperators(expr) { - // 先处理开头的负号 - if (expr.startsWith('-')) { - const restExpr = expr.substring(1); - if (/^\d+/.test(restExpr)) { - // 负数 - const match = restExpr.match(/^\d+/); - const number = match[0]; - const remaining = restExpr.substring(number.length); - return (-parseInt(number)) + remaining; - } else if (/^[a-zA-Z_]\w*/.test(restExpr)) { - // 负变量 - const match = restExpr.match(/^[a-zA-Z_]\w*/); - const varName = match[0]; - const remaining = restExpr.substring(varName.length); - if (this.variables.has(varName)) { - return (-this.variables.get(varName)) + remaining; - } - } - } - - return expr; - } - - // 解析乘法、除法和模运算(高优先级) - parseMultiplicationDivision(expr) { - const tokens = this.tokenizeExpression(expr, ['*', '/', '%']); - let result = this.evaluateToken(tokens[0]); - - for (let i = 1; i < tokens.length; i += 2) { - const operator = tokens[i]; - const operand = this.evaluateToken(tokens[i + 1]); - - if (operator === '*') { - result *= operand; - } else if (operator === '/') { - result = Math.floor(result / operand); // C++整数除法 - } else if (operator === '%') { - result = result % operand; // 模运算 - } - } - - return result; - } - - // 解析加法和减法(低优先级) - parseAdditionSubtraction(expr) { - const tokens = this.tokenizeExpressionSmart(expr, ['+', '-']); - let result = this.evaluateToken(tokens[0]); - - for (let i = 1; i < tokens.length; i += 2) { - const operator = tokens[i]; - const operand = this.evaluateToken(tokens[i + 1]); - - if (operator === '+') { - result += operand; - } else if (operator === '-') { - result -= operand; - } - } - - return result; - } - - // 智能分词器,考虑到负号可能是一元运算符 - tokenizeExpressionSmart(expr, operators) { + lexArithmetic(expr) { const tokens = []; - let current = ''; let i = 0; - + const isIdChar = (ch) => /[a-zA-Z0-9_.]/.test(ch); + while (i < expr.length) { - const char = expr[i]; - - if (operators.includes(char)) { - // 检查是否是一元负号 - if (char === '-' && (i === 0 || operators.includes(expr[i-1]) || expr[i-1] === '(')) { - // 这是一元负号,继续收集 - current += char; - } else { - // 这是二元运算符 - if (current.trim()) { - tokens.push(current.trim()); - current = ''; - } - tokens.push(char); + const ch = expr[i]; + if (/\s/.test(ch)) { + i++; + continue; + } + if ('+-*/%()'.includes(ch)) { + tokens.push({ type: ch }); + i++; + continue; + } + if (/\d/.test(ch)) { + let num = ''; + while (i < expr.length && /\d/.test(expr[i])) { + num += expr[i++]; } - } else { - current += char; + tokens.push({ type: 'num', value: parseInt(num, 10) }); + continue; + } + if (/[a-zA-Z_]/.test(ch)) { + let id = ''; + while (i < expr.length && isIdChar(expr[i])) { + id += expr[i++]; + } + if (expr[i] === '(') { + let depth = 0; + let call = id; + while (i < expr.length) { + const c = expr[i++]; + call += c; + if (c === '(') depth++; + else if (c === ')') { + depth--; + if (depth === 0) break; + } + } + tokens.push({ type: 'call', value: call }); + } else { + tokens.push({ type: 'id', value: id }); + } + continue; } i++; } - - if (current.trim()) { - tokens.push(current.trim()); - } - return tokens; } - // 将表达式分解为标记 - tokenizeExpression(expr, operators) { - const tokens = []; - let current = ''; - - for (let i = 0; i < expr.length; i++) { - const char = expr[i]; - - if (operators.includes(char)) { - if (current.trim()) { - tokens.push(current.trim()); - current = ''; - } - tokens.push(char); + peekExprToken() { + return this._exprTokens[this._exprPos] || { type: 'eof' }; + } + + consumeExprToken() { + return this._exprTokens[this._exprPos++] || { type: 'eof' }; + } + + parseAddExpr() { + let left = this.parseMulExpr(); + while (this.peekExprToken().type === '+' || this.peekExprToken().type === '-') { + const op = this.consumeExprToken().type; + const right = this.parseMulExpr(); + left = op === '+' ? left + right : left - right; + } + return left; + } + + parseMulExpr() { + let left = this.parseUnaryExpr(); + while (['*', '/', '%'].includes(this.peekExprToken().type)) { + const op = this.consumeExprToken().type; + const right = this.parseUnaryExpr(); + if (op === '*') { + left *= right; + } else if (op === '/') { + left = right === 0 ? 0 : Math.trunc(left / right); } else { - current += char; + left %= right; } } - - if (current.trim()) { - tokens.push(current.trim()); - } - - return tokens; + return left; } - // 计算单个标记的值 - evaluateToken(token) { - token = token.trim(); - if (!token) return 0; - - // 如果包含乘除法或模运算,交给乘除法解析器处理 - if (token.includes('*') || token.includes('/') || token.includes('%')) { - return this.parseMultiplicationDivision(token); + parseUnaryExpr() { + const type = this.peekExprToken().type; + if (type === '-') { + this.consumeExprToken(); + return -this.parseUnaryExpr(); } - - // 布尔常量 + if (type === '+') { + this.consumeExprToken(); + return this.parseUnaryExpr(); + } + return this.parsePrimaryExpr(); + } + + parsePrimaryExpr() { + const token = this.peekExprToken(); + if (token.type === 'num') { + this.consumeExprToken(); + return token.value; + } + if (token.type === 'id') { + this.consumeExprToken(); + return this.evaluateAtom(token.value); + } + if (token.type === 'call') { + this.consumeExprToken(); + return this.evaluateAtom(token.value); + } + if (token.type === '(') { + this.consumeExprToken(); + const value = this.parseAddExpr(); + if (this.peekExprToken().type === ')') { + this.consumeExprToken(); + } + return value; + } + debugLog(`⚠️ 无法解析标记: ${token.type}`); + return 0; + } + + evaluateAtom(token) { if (token === 'true') return 1; if (token === 'false') return 0; - - // 数字 - if (/^-?\d+$/.test(token)) { - return parseInt(token); - } - - // 变量查找 + if (this.variables.has(token)) { return this.variables.get(token); } - - // 对象属性访问 (如 pos.x, pos.y, pos.z) + if (token.includes('.')) { const [objName, propName] = token.split('.'); if (this.variables.has(objName)) { @@ -2591,8 +2551,7 @@ export class CppCodeExecutor { } } } - - // 游戏API调用 + if (token.includes('player_data(')) { const match = token.match(/player_data\s*\(\s*"(.+?)"\s*\)/); if (match) return window.cppGameAPI.player_data(match[1]); @@ -2604,7 +2563,7 @@ export class CppCodeExecutor { if (token.includes('vehicle_position()')) { return window.cppGameAPI.vehicle_position(); } - + debugLog(`⚠️ 无法解析标记: ${token}`); return 0; }