开发Mount&Blade: Warband脚本格式化插件遇unexpected token(1:5)错误
Warband脚本语言VS Code格式化工具报错修复
我正在为《骑马与砍杀:战团》的Warband Script Language开发VS Code代码格式化工具和代码片段支持,目前代码片段功能正常,但格式化工具完全无法工作。按下快捷键alt+shift+f触发格式化时,VS Code弹出unexpected token(1:5)错误,此时执行的是mbap.formatWarbandScript命令。
预期实现效果:
operationNames数组中每个元素对应的语句后换行并添加缩进- 结束语句
try_end与起始语句对齐
extension.js
const acorn = require('acorn'); const escodegen = require('escodegen'); const vscode = require('vscode'); function formatWarbandScriptLanguageCode(code) { const parsedAst = acorn.parse(code, { ecmaVersion: 5 }); let indentationLevel = 0; traverse(parsedAst, { enter(node, parent) { if (node.type === 'CallExpression') { const operationNames = [ 'try_begin', 'try_for_range', 'try_for_range_backwards', 'try_for_parties', 'try_for_agents', 'try_for_prop_instances', 'try_for_players', 'try_for_dict_keys', ]; if (operationNames.includes(node.callee.name)) { // Insert a newline before the operation call if (parent.body.indexOf(node) === 0) { const newlineNode = { type: 'WhiteSpace', value: '\n' + ' '.repeat(indentationLevel), // Adjust the desired indentation }; parent.body.unshift(newlineNode); } // Add a tab indentation to the arguments of the operation node.arguments.forEach(arg => { if (arg.type === 'ArrayExpression') { arg.elements.forEach(element => { element.loc.indent += 1; // Adjust the indentation level }); } }); indentationLevel++; } } }, leave(node) { if (node.type === 'CallExpression') { const operationNames = [ 'try_begin', 'try_for_range', 'try_for_range_backwards', 'try_for_parties', 'try_for_agents', 'try_for_prop_instances', 'try_for_players', 'try_for_dict_keys', ]; if (operationNames.includes(node.callee.name)) { indentationLevel--; } } }, }); const formattedCode = escodegen.generate(parsedAst); return formattedCode; } function activate(context) { console.log('M&B Warband API extension is now active.'); // ... other activation code ... let disposable = vscode.commands.registerCommand('mbap.formatWarbandScript', () => { const editor = vscode.window.activeTextEditor; if (!editor) { return; } const document = editor.document; const text = document.getText(); // Format the code const formattedCode = formatWarbandScriptLanguageCode(text); // Apply the formatted code const edit = new vscode.TextEdit( new vscode.Range(0, 0, document.lineCount, 0), formattedCode ); const workspaceEdit = new vscode.WorkspaceEdit(); workspaceEdit.set(document.uri, [edit]); vscode.workspace.applyEdit(workspaceEdit); }); context.subscriptions.push(disposable); } // This method is called when your extension is deactivated function deactivate() { console.log('M&B Warband API extension is now deactivated.'); } module.exports = { activate, deactivate };
package.json
{ "name": "mbap", "displayName": "M&B: Warband API", "description": "Mount & Blade: Warband language support for Microsoft Visual Studio Code by Azremen and Sart", "publisher": "Azremen", "version": "0.1.18", "homepage": "https://github.com/Azremen/MB-Warband-API-VSC/blob/main/README.md", "engines": { "vscode": "^1.62.0" }, "repository": { "type": "git", "url": "https://github.com/Azremen/MB-Warband-API-VSC.git" }, "categories": [ "Programming Languages" ], "main": "./extension.js", "activationEvents": [ "onCommand:mbap.formatWarbandScript" ], "contributes": { "languages": [ { "id": "warbandsl", "aliases": [ "Warband Script Language" ], "extensions": [ ".py" ], "configuration": "./language-configuration.json" } ], "commands": [ { "command": "mbap.formatWarbandScript", "title": "Format Warband Script Language Code" } ], "snippets": [ { "language": "warbandsl", "path": "./snippets/mbap.code-snippets" } ], "keybindings": [ { "command": "mbap.formatWarbandScript", "key": "alt+shift+f", "when": "editorTextFocus && resourceLangId == warbandsl" } ] }, "configuration": { "title": "Warband Script Language Formatter", "properties": { "warbandsl.indentation": { "type": "string", "enum": [ "tabs", "spaces" ], "default": "spaces", "description": "Indentation style for Warband Script Language Formatter." }, "warbandsl.tabSize": { "type": "number", "default": 4, "description": "Number of spaces or tabs to use for indentation." } } }, "dependencies": { "acorn": "^8.0.1", "escodegen": "^2.0.0" } }
问题根源与修复方案
1. 核心错误原因
unexpected token(1:5)错误是因为你用JavaScript专用解析器acorn去解析Warband脚本语言,两者语法完全不兼容。Warband脚本的语法(如(try_begin)、无括号的函数调用)不符合JavaScript规范,acorn无法识别,直接抛出解析错误。
2. 针对性修复步骤
步骤1:替换解析方案,放弃acorn/escodegen
acorn只适用于JavaScript,无法处理自定义脚本语言。你有两种选择:
- 手动编写Warband脚本的词法分析器和语法分析器,或者用通用解析工具(如nearley.js)定义语法规则
- 用正则表达式/字符串匹配实现轻量级格式化(适合Warband脚本这种语法相对简单的场景)
步骤2:修复traverse函数未定义问题
代码中调用的traverse函数属于acorn-walk模块,你没安装也没导入,会导致运行时错误。如果坚持用acorn生态,需要:
npm install acorn-walk
然后在extension.js头部添加:
const { traverse } = require('acorn-walk');
步骤3:实现Warband脚本专属格式化逻辑
以下是基于字符串匹配的轻量格式化实现,能满足你的预期需求:
function formatWarbandScriptLanguageCode(code) { let indentLevel = 0; const indent = ' '; const lines = code.split('\n').map(line => line.trim()); const formattedLines = []; for (const line of lines) { if (!line) continue; // 处理结束语句,先减缩进再添加 if (line.startsWith('(try_end')) { indentLevel = Math.max(0, indentLevel - 1); formattedLines.push(indent.repeat(indentLevel) + line); } else { // 添加当前行 formattedLines.push(indent.repeat(indentLevel) + line); // 处理起始语句,增加缩进 const operationNames = [ 'try_begin', 'try_for_range', 'try_for_range_backwards', 'try_for_parties', 'try_for_agents', 'try_for_prop_instances', 'try_for_players', 'try_for_dict_keys' ]; for (const op of operationNames) { if (line.startsWith(`(${op}`)) { indentLevel++; break; } } } } return formattedLines.join('\n'); }
步骤4:完善VS Code格式化配置
在package.json的contributes中添加格式化器声明,让VS Code自动关联:
"contributes": { // ... 现有内容 ... "formatters": [ { "language": "warbandsl", "id": "mbap.warbandslFormatter", "label": "Warband Script Language Formatter" } ] }, "activationEvents": [ "onCommand:mbap.formatWarbandScript", "onLanguage:warbandsl" ]
更规范的做法是实现DocumentFormattingEditProvider接口,替代当前的命令触发方式。
内容的提问来源于stack exchange,提问作者Gökberk Alkış
相关产品推荐
相关产品推荐

