UNPKG

godprotocol

Version:
381 lines (333 loc) 12 kB
import Callable from "./Callable"; class Virtual_machine extends Callable { constructor(account) { super(); this.account = account; this.contexts = new Array(); } read_config = async (addr, options = {}) => { if (typeof addr !== "string") { return addr; } let oracle = this.account.manager.oracle; let result = await oracle.read(addr, { current: true, config: true }); return result; }; sync = async () => {}; errors = ['MathError', 'ZeroDivision', 'RuntimeError'] handle_error = async(errline, {config, chain})=>{ let {objects} = config; for (let o=0; o< objects.length; o++){ let err = objects[o], caught, line = errline; if (line.value.type === 'variable'){ if (this.errors.includes(line.value.value)){ line = {value: {name: line.value.value}} }else { let res= await this.execute(line.value, {chain, caught: true}) if (res.type === 'address'){ res = await this.resolve_address(res.value) } line = res; } }else if(typeof line.value === 'string' || line.value.type){ let res = await this.execute(line.value, {chain, caught: true, cloth:true}) line = res; } if(line){ if (line.type === 'instance'){ let classifier = await line.get('_classifier_') caught = err === classifier if (config.alias){ let conf = await this.read_config(config.alias); let hash = await this.account.manager.oracle.hash(conf); await this.execute({...conf, value: {type: 'address', value: line.value.path}}, {chain, hash}) } }else if(!line.type){ caught = err.endsWith(line.value.name) } } if(caught){ await this.execute_body(config.catch_body, {chain, config: {...config, caught: true}}) return true; } } } execute_body = async (body, options)=>{ let {config, chain} = options; let result; for (let b=0; b< body.length; b++){ let line = body[b]; line = await this.execute(line, {...options, line}); if (line){ if (line.interrupt_loop){ result =line break; } else if (line.done){ result = line.value; break; }else if (line.exit){ result = line; break }else if (line.error){ if (config.type === 'trycatch'){ let res = !config.caught && await this.handle_error(line, {chain, config}) if(!res){ result = line } break }else { result = line; break } } result = line } } return result; } execute = async (config, options = {}) => { let prev_config = config config = await this.read_config(config); // console.log(config, 'EXE') let { chain, cloth, hash, obj, call_config } = options, result; hash = hash || (await this.account.manager.oracle.hash(config)); switch (config.type) { case "module": result = await this.execute_body(config.body, {chain, config}) break; case "class": if (call_config) { let instance_chain = await chain.chain(call_config.location); let object = { content: [${config.name}], _classifier_: config.address, uid: this.gen_uid(), }; for (let name in config.methods) { let addr = config.methods[name]; object[name] = addr; } let res = await instance_chain.write(object, { is_instance: true }); result = { type: "address", value: res }; if (object._init_) { await this.execute( { ...call_config, object: result, identifier: object._init_ }, { chain } ); } } break; case 'adm': let props = await this.execute(config.props, {chain}) if(config.name.includes('/')){ result = await this.execute({ type:'call', identifier: config.name, arguments: [{...props, position: 0}], location: config.location }, {chain}) }else { result = await this.native_components(config, chain) result = await this.parse_aircode(result, {config, chain}) } break case "function": if (call_config) { let fn_chain = await chain.chain(config.address); let params = new Array(); for (let a = 0; a < call_config.arguments.length; a++) { let arg = call_config.arguments[a]; for (let p = 0; p < config.parameters.length; p++) { let param = await this.read_config(config.parameters[p]); if (arg.identifier) { } else { if (p === arg.position) { param.hash = await this.account.manager.oracle.hash(param); param.value = arg; params.push(param); } } } } for (let p = 0; p < params.length; p++) { let param = params[p]; await this.execute(param, { chain: fn_chain, hash: param.hash }); } if (call_config.object) { let conf = { type: "assignment", identifier: "this", value: call_config.object, }; let r = await fn_chain.add_config(conf); await this.execute(r, { chain: fn_chain }); } result = await this.execute_body(config.body, {chain: fn_chain, config}) }else result = {type:'address', value: prev_config} break; case "return": let ret_value = await this.execute(config.output, { chain }); result = { done: true, value: ret_value }; break; case "assignment": let ass_chain = await chain.chain(config.identifier); let value = await this.execute(config.value, { chain: ass_chain }); if (value && value.error){ result = value }else { if (value && value.account) { value = { type: "address", value: value.value.path }; } else if (typeof value === "string") value = { type: "address", value }; result = await ass_chain.write(value, { hash }); } break; case "call": if (config.identifier.type === "nest") { config.identifier = await this.execute(config.identifier.address, { chain, }); } for (let a = 0; a < config.arguments.length; a++) { let arg = await this.read_config(config.arguments[a]); let obj = await this.execute( arg.type === "assignment" ? arg.value : arg, { chain } ); if (obj.account) { obj = { type: "address", value: obj.value.path }; } if (arg.type === "assignment") { obj.identifier = arg.identifier; } obj.position = a; config.arguments[a] = obj; } result = await this.execute_call(config, { chain , obj}); break; case 'loop': let {loop_type } = config; if (loop_type === 'for'){ await this.execute(config.iterator, {chain}) } let cond = await this.execute(config.condition, {chain , cloth: true}) while(await cond.literal()){ result = await this.execute_body(config.body, {config, chain}) if (result && result.interrupt_loop){ if (result.interrupt_loop === 'break'){ result = null break; } else{ result = null } } if(loop_type === 'for'){ await this.execute(config.update, {chain}) } cond = await this.execute(config.condition, {chain , cloth: true}) } break case 'trycatch': result = await this.execute_body(config.try_body, {chain, config}) break; case 'raise': result = {error: true, value: config.object} break; case "condition": for (let i = 0; i < config.blocks.length; i++) { let block = config.blocks[i]; let pred = await this.execute(block.predicate, { chain, cloth: true, }); if (await pred.literal()) { result = await this.execute_body(block.body, {chain, config}) break; } } break; case "nest": let prev = await this.execute(config.nests[0], { chain, cloth: true }), pprev; for ( let n = 1; n < config.nests.length - (config.assignment ? 1 : 0); n++ ) { let nst = await this.read_config(config.nests[n]); pprev = prev; if (nst.type === "call") { let conf =prev.type === 'function'? prev.value: await prev.get(nst.identifier, {obj: prev, config: nst, chain}); if (!conf) await this.throw_error(); nst.object = { value: prev.value.path, type: "address" }; conf.prev_name = nst.identifier prev = await this.execute( { ...nst, identifier: conf.type ==='function'?conf.address:conf }, { chain, cloth: true, obj:prev } ); if(prev && prev.error){ break; } } else { let res = await this.execute(nst, { chain, cloth: true }); let prop = await res.literal(); if(prev.type === 'address'){ let conf = await this.resolve_address(prev.value) prev = await this.cloth_content(conf) } prev = await prev.get(prop, {obj: res, config: nst, chain}); } } if (config.assignment) { let ass = await this.execute(config.assignment, { chain }); let last = config.nests.slice(-1)[0]; last = await this.execute(last, { chain, cloth: true }); await prev.set(last, ass); } if (prev && prev.type !== "address") { if(!prev.error) prev = { value: prev.value ? prev.value.path : prev, type: "address", object: prev.value ? null : pprev.value.path, }; } result = prev; break; default: if (["number", "string", "boolean", 'twain', 'void', 'array'].includes(config.type)) { result = await this.instantiate(config, { chain, hash }); result = { type: "address", value: result }; } else if (config.type === "address") { result = config; } else if (config.type === "variable") { let var_chain = await chain.chain(config.location); let res = await var_chain.read({ sibling: true }); if (!res) { res = await this.account.manager.oracle.fs.read( ${var_chain.path}/.config ); res = { type: "address", value: res.current }; } result = res; } else if (config.type === "reference") { let oracle = this.account.manager.oracle; let i_chain = await chain.chain(config.value); let conf = await oracle.fs.read(${i_chain.path}/.config); result = { type: "address", value: conf.current }; }else if (['continue', 'break'].includes(config.type)){ result = {interrupt_loop: config.type} } } if (result && cloth) { if ( result.type === "address" && result.value.includes('/')) { result = await this.resolve_address(result.value); } if (result && !result.error) result = await this.cloth_content(result); } return result; }; } export default Virtual_machine;