godprotocol
Version:
A distributed computing environment
381 lines (333 loc) • 12 kB
JavaScript
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;