实现原理
解析blockly语法树,使用js管理状态,实际使用lua执行,c/c++函数调用使用lua调用c/c++函数的能力
可以单行执行
已实现if功能
TODO
for循环功能 函数功能
单步执行效果图
直接执行效果图
源代码
//0 暂停 1 单步执行 2 断点
//创建枚举
var AstStatus = {PAUSE : 0,STEP :1,BREAK : 2,PASS:3
}class ASTInterpreter {constructor(ast,funcs) {//执行状态,暂停 单步执行 断点this.status = AstStatus.STEP;//当前节点this.currentBlock = null;this.ast = ast;this.variables = {};this.userFuns={};this.functions=new Set();this.initVariables();this.initFunc(funcs);this.callback=console.log}addEventCallback(func){this.callback=func}initFunc(funcs){for (let item of funcs) {this.addFunction(item)}}initVariables() {let vars = this.ast.variables;for (let i = 0; i < vars.length; i++) {const varDef = vars[i];const varId = varDef.id;const varName = varDef.name;this.variables[varId] = varName;}}stepFunc(){this.executeBlock(this.currentBlock)}addFunction(key) {this.functions.add(key);}getBlockType(block){let type = block.typeif(this.functions.has(type)){//说明是自定义函数return 'function'}else{return type}}execute() {if (this.ast && this.ast.blocks && this.ast.blocks.blocks) {const blocks = this.ast.blocks.blocks;for (let i = 0; i < blocks.length; i++) {const block = blocks[i];this.executeBlock(block);}}}executeBlock(block) {if (!block) return;let btype=this.getBlockType(block)let code = ''switch (btype) {case 'variables_set'://变量idconst varId = block.fields.VAR.id;//解析右值const value = this.evaluateInput(block.inputs.VALUE.block);code +=this.variables[varId]+'='+value;break;case 'function':code += this.executeFunction(block);break;case 'controls_if':this.executeIf(block);break;case 'procedures_callnoreturn':this.executeCallNoReturn(block);break;case 'procedures_defnoreturn':this.executeDefNoReturn(block);break;default:console.error(`Unsupported block type: ${block.type}`);}if(code){this.execLine(code,block.id)}// Execute next blockif (block.next && block.next.block) {this.currentBlock = block.next.block;if(this.status == AstStatus.STEP||this.status == AstStatus.PAUSE){}else{this.executeBlock(block.next.block);}}}//自定义函数定义 用户自己定义的函数不是系统的函数executeDefNoReturn(block){this.userFuns[block.fields.NAME]={params:block.extraState.params,block:block.inputs.STACK.block}return ''}//调用自定义函数 调用自己定义的函数executeCallNoReturn(block){//这里要隔离,不能影响其他变量,函数参数怎么传递,返回值怎么传递是个问题let extraInfo = block.extraStatelet name = extraInfo.namelet args = block.inputsreturn ''}async executeIf(block) {let extraInfo = block.extraStatelet ifprefix = 'IF'let doprefix = 'DO'let isElse = falselet isEIf = falseif(extraInfo){//说明有elseisElse=trueif(extraInfo.elseIfCount){//说明有else ifisEIf = true}}let idx=[0];if(isEIf){//extraInfo.elseIfCount是一个int类型,我要直线这个int类型的值for (let i = 0; i < extraInfo.elseIfCount; i++) {idx.push(i+1)}}let isMatch = falsefor (let i = 0; i < idx.length; i++) {const index = idx[i];const condition = await this.evaluateCondition(block.inputs[ifprefix + index].block)if (condition) {//满足条件执行对应分支this.executeBlock(block.inputs[doprefix + index].block)isMatch=truebreak}}//说明都不匹配 执行else blockif(isElse&&!isMatch){this.executeBlock(block.inputs.ELSE.block)}return ''}executeFunction(block){if (!block) return;let funcName=block.typelet params=block.inputslet paramArr=[]for(let key in params){let param=params[key]if(param.block){let paramValue=this.evaluateInput(param.block)paramArr.push(paramValue)}}//解析函数的参数名//执行自定义函数console.log(1,params)return `${funcName}(${paramArr.join(',')})`}generateMixed(n) {let chars = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];let res = "";for(let i = 0; i < n ; i++) {let id = Math.floor(Math.random()*chars.length);res += chars[id];}return res;}execLine(code,id){//发送到服务器获取结果//执行事件this.callback({id,name:'call'})console.log('exec '+code)}async evaluateCondition(block){if (!block) return;let op = block.fields.OPlet left = this.evaluateInput(block.inputs.A.block)let right = this.evaluateInput(block.inputs.B.block)//发送到服务器获取结果//随机生成变量,服务器会返回这个变量值,这个变量值是boolean类型let key = this.generateMixed(5)switch (op) {case 'EQ':op = '=='break;case 'NEQ':op = '!='break;case 'LT':op = '<'break;case 'LTE':op = '<='break;case 'GT':op = '>'break;case 'GTE':op = '>='break;default:console.error(`Unsupported condition operator: ${op}`);break;}var code = `${key} = (${left} ${op} ${right})`var id=block.idawait this.callback({id,name:'call'})console.log('eval '+code)return true}evaluateInput(input) {if (!input) return null;let block = input;let btype=this.getBlockType(block)switch (btype) {case 'text'://普通text 常量节点return `'${block.fields.TEXT}'`;case 'math_number':return block.fields.NUM;case 'variables_get'://变量赋值给变量const varId = block.fields.VAR.id;return this.variables[varId];//返回变量的名字case 'function'://函数的返回值赋值给变量return this.executeFunction(block); // Recursively execute custom typesdefault:console.error(`Unsupported input block type: ${block.type}`);return null;}}}// Example usagevar funs = ['create_mat','create_hobj','write_mat','printlog','test_tpl']
const interpreter = new ASTInterpreter(ast,funs);interpreter.execute();