/** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 4-1-16 by sbosse. ** $VERSION: 1.8.1 ** ** $INFO: ** ** JAM Agent Compiler ** ** $ENDOFINFO */ var onexit=false; var start=false; global.config={simulation:false}; Require('os/polyfill') var Io = Require('com/io'); var Comp = Require('com/compat'); var Db = Require('db/db'); var Aios = Require('jam/aios'); var Esprima = Require('parser/estprima'); var Escodegen = Require('printer/estcodegen'); var Buf = Require('dos/buf'); var Net = Require('dos/network'); var Dns = Require('dos/dns'); var Cs = Require('dos/capset'); var Run = Require('dos/run'); var Rpc = Require('dos/rpc'); var Std = Require('dos/std'); var Router = Require('dos/router'); var Sch = Require('dos/scheduler'); var Conn = Require('dos/connection'); var Json = Require('jam/jsonfn'); var Dios = Require('dos/dios'); var Status = Net.Status; var Command = Net.Command; var util = Require('util'); var path = Require('path'); Run.current(Aios); var env = {}; function className(filename) { return path.basename(filename, path.extname(filename)) } var options = { amp:false, aip:'localhost', aport:10002, bip:'localhost', bport:3001, broker:false, debug:false, default:true, dip : 'localhost', dports : [], env:{}, geo:undefined, hostport:undefined, http:false, keepalive:true, myip:'localhost', monitor:0, output:undefined, print:false, repeat:0, tcpnet:0, verbose:0 }; var out = function (msg) { Io.out('[JAC] '+msg)}; var jamc = function(options) { var self=this; this.options=checkOption(options,{}); this.env=checkOption(options.env,{}); this.classes={}; this.out=out; this.err=function (msg,err) { out('Error: '+msg); throw (err||'Error'); } this.warn=function (msg) { out('Warning: '+msg); } this.pending=false; this.bport=checkOption(options.bport,3001); this.bip=options.bip; this.amp=options.amp; this.aport=options.aport; this.hostname=Io.hostname(); this.nodename=options.nodename; this.options.privhostport=Net.uniqport(); this.options.pubhostport = Net.prv2pub(this.options.privhostport); this.scheduler=Sch.TaskScheduler(); this.todo=[]; this.verbose=checkOption(options.verbose,0); }; // Import analyzer class... var JamAnal = Require('jam/analyzer'); JamAnal.current(Aios); jamc.prototype.analyzeSyntax=JamAnal.jamc.prototype.analyze; jamc.prototype.syntax=JamAnal.jamc.prototype.syntax; /** Add an agent class template {:} to the JAM world * */ jamc.prototype.addClass = function (name,ac) { if (this.verbose) this.out('Added agent class '+name); this.classes[name]=ac; }; jamc.prototype.compile = function (filename, options) { var res = this.open(filename,options); if (res) for(var p in res) { this.compileClass(p,res[p],{verbose:this.verbose}); } } /** Compile (analyze) an agent class constructor function and add it to the world class library. ** Can be used after an open statement. ** Usage: compileClass(name,constructor,options?) ** compileClass(constructor,options?) ** ** typeof @name=string|undefined ** typeof @constructor=function|string ** typeof @options={verbose:number|boolean)|number|undefined */ jamc.prototype.compileClass = function (name,constructor,options) { var ac,p,verbose,content,syntax,report,text,env={ac:undefined},self=this,ac; if (typeof name == 'function') constructor=name,name=undefined,options=constructor; if (typeof options == 'object') verbose=options.verbose; else if (options!=undefined) verbose=options; else verbose=this.verbose; // if (typeof constructor != 'function') throw 'compileClass: second constructor argument not a function'; if (typeof constructor == 'function') text = constructor.toString(); else text = constructor; if (!name) { // try to find name in function definition name=text.match(/[\s]*function[\s]*([A-Za-z0-9_]+)[\s]*\(/); if (!name) throw ('compileClass: No class name provided and no name found in constructor '+ text.substring(0,80)); name=name[1]; } content = 'var ac = '+text; try { syntax = Esprima.parse(content, { tolerant: true, loc:true }) } catch (e) { throw 'compileClass('+name+'): Parsing failed with '+e } report = this.analyzeSyntax(syntax, { classname:name, level:2, verbose:verbose||0, err: function (msg){self.err(msg)}, out: function (msg){self.out(msg)}, warn: function (msg){self.warn(msg)} }); if (report.errors.length) { throw 'compileClass('+name+'): failed with '+report.errors.join('; ')}; for (p in report.activities) env[p]=p; with (env) { eval(content) }; ac=env.ac; env.ac=undefined; this.addClass(name,ac,env); return name; } /** Create and return JSON+ agent from class ac with arguments. * */ jamc.prototype.generate = function (ac,args) { var node=this.node; var code=none; if (this.options.verbose>0) this.out('Compiling agent from class '+ac+' ['+args+'] ..'); if (this.classes[ac]) code = Aios.Code.createAndReturn(this.classes[ac],ac,args); else this.out('generate: Cannot find agent class '+ac+'.'); return code; }; /** Get an agent class template {:} from the JAM world * */ jamc.prototype.getclass = function (name) { var ac={}; ac[name]=this.classes[name]; if (ac[name]) return ac; else return undefined; }; /** Initialize broker or amp connections ... * */ jamc.prototype.init=function () { var self=this; this.scheduler.Init(); if (this.options.broker||this.options.dports.length>0) { this.network = Conn.setup(this.options,1); this.router = this.network.router; this.rpc = this.network.rpc; this.std = Std.StdInt(this.rpc); this.dns = Dns.DnsInt(this.rpc); this.cs = Cs.CsInt(this.rpc); this.run = Run.RunInt(this.rpc); this.dios = Dios.Dios(this.rpc,this.env); } if (this.amp) { this.startamp(); } } /** Read and parse one agent class from file. Can contain nested open statements. * File/source text format: function [ac] (p1,p2,..) { this.x; .. ; this.act = {..}; ..} * open(file:string,options?:{verbose?:number|boolean,classname?:string}) -> function | object * * Output can be processed by method compileClass */ jamc.prototype.open = function (filename,options) { var self=this, res, text, name, ast=null; options=checkOptions(options,{}); name=checkOption(options.classname,className(filename)); if (this.verbose>0) this.out('Opening file '+filename); function parseModel (text) { var modu={},more,module={exports:{}},name=text.match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/); if (name) name=name[1]; function open(filename) { var text; try { if (self.verbose>0) self.out('Opening file '+filename); text=Io.read_file(filename); if (self.verbose) self.out('%% '+text.length+' bytes'); return parseModel(text); } catch (e) { self.err('Opening of '+file+' failed: '+e); } } try { with (module) {eval('res = '+text)}; if (name) { modu[name]=res; return modu} else if (module.exports) return module.exports; else return res; } catch (e) { try { ast = Esprima.parse(text, { tolerant: true, loc:true }); if (ast.errors && ast.errors.length>0) more = ', '+ast.errors[0]; } catch (e) { if (e.lineNumber) more = ', in line '+e.lineNumber; } self.out(e.name+(e.message?': '+e.message:'')+(more?more:'')); } } try { text=Io.read_file(filename); if (this.verbose) this.out('%% '+text.length+' bytes'); return parseModel(text); } catch (e) { this.err('Opening of '+file+' failed: '+e); } }; /** ESTPRIMA/ESTCODEGEN Test */ jamc.prototype.print = function (file,depth) { var code, text, ast; if (Io.exists(file)) { if (this.options.verbose>=0) this.out('Parsing Javascript from file '+file+' ..'); text=Io.read_file(file); } else { if (this.options.verbose>=0) this.out('Parsing Javascript from string "'+file+'" ..'); text=file; // Is it text string? } try { ast=Esprima.parse(text, { tolerant: true, loc:true }); console.log(util.inspect(ast.body,{depth:depth})); code=Escodegen.generate(ast); console.log(code); } catch (e) { console.log(e); } }; /** Send request to a JAM node ** function (data:string,dst:string,cmd:Net.Command*) */ jamc.prototype.send = function (data,dst,cmd) { var self=this, cap,stat; if (Comp.string.isNumeric(dst)) dst='localhost:'+dst; if (this.options.amp) myJamc.todo.push([ function () { var addr = self.amp.url2addr(dst), buf=Buf.Buffer(), context=Sch.Suspend(); Buf.buf_put_string(buf,data); self.amp.request(cmd,buf,function (res) { Sch.Wakeup(context); },addr.address,addr.port); } ]) else if (this.options.broker) { myJamc.todo.push([ function () { self.dios.resolve(dst,function (_cap,_stat) { cap=_cap; stat=_stat; }); }, function () { if (stat==Status.STD_OK) switch (cmd) { case Command.PS_WRITE: self.run.ps_write(cap,data,function (_stat) { self.out('Finished request '+Net.Print.command(cmd)+' to '+dst+' with '+Net.Print.status(_stat)); }); break; case Command.PS_EXEC: self.run.ps_exec(cap,data,function (_stat) { self.out('Finished request '+Net.Print.command(cmd)+' to '+dst+' with '+Net.Print.status(_stat)); }); break; case Command.PS_STUN: self.run.ps_stun(cap,data,function (_stat) { self.out('Finished request '+Net.Print.command(cmd)+' to '+dst+' with '+Net.Print.status(_stat)); }); break; } else self.out('Send: Invalid destination '+dst+': '+Net.Print.status(stat)); } ]) } else self.out('Send: No route to destination '+dst); self.pending=true; } jamc.prototype.setenv=function (name,value) { var self=this; switch (name) { case 'DNS': self.env.dns_def=value; self.env.rootdir=self.env.workdir=self.cs.cs_singleton(value); break; case 'AFS': self.env.afs_def=value; break; } } /** Start the router and scheduler * */ jamc.prototype.start=function () { this.running=true; this.todo.push([ function () {Io.out('[JAMC] Finished. Exit.'); Io.exit()} ]); this.todo=Comp.array.flatten(this.todo); B(this.todo); // Start up the network .. if (this.network) B([this.network.init,this.network.start]); // this.out('Starting ..'); Sch.ScheduleNext(); Sch.ScheduleNext(); } /** Start an AMP service port TODO */ jamc.prototype.startamp = function () { var self=this; var ip = 'localhost'; this.out('Starting AMP server on port '+this.aport); this.amp = Aios.Chan.Amp({rcv:ip+':'+this.aport,snd_ip:ip,verbose:this.options.verbose}); this.amp.receiver(function (handler) { if (handler) { if (self.options.verbose>1) { self.out('AMP: got request:'); console.log(handler) }; } }); } var Jamc = function(options) { var obj = new jamc(options); return obj; } function usage(exit) { var msg='AgentJS Compiler and Agent Management Program'+NL; msg += 'usage: jamc [options]'+NL; msg += 'compile -c