From 9d3a1f786ce4321ea6c4ca5b63011470e488f364 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 23:19:34 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/top/jamc.js | 662 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 662 insertions(+) create mode 100644 js/top/jamc.js diff --git a/js/top/jamc.js b/js/top/jamc.js new file mode 100644 index 0000000..15dc272 --- /dev/null +++ b/js/top/jamc.js @@ -0,0 +1,662 @@ +/** + ** ============================== + ** 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