remove global

This commit is contained in:
2025-12-18 18:43:23 -06:00
parent aa18a8c8d2
commit d50f4119ee
60 changed files with 378 additions and 282 deletions

4
add.ce
View File

@@ -7,7 +7,7 @@ if (args.length < 1) {
log.console("Examples:")
log.console(" cell get gitea.pockle.world/john/prosperon@main")
log.console(" cell get github.com/user/repo@v1.0.0 myalias")
$_.stop()
$stop()
return
}
@@ -16,4 +16,4 @@ var alias = args.length > 1 ? args[1] : null
shop.get(locator, alias)
$_.stop()
$stop()

View File

@@ -40,4 +40,4 @@ function bottomUpTree(depth) {
mainThread()
$_.stop()
$stop()

View File

@@ -22,4 +22,4 @@ for (var i = 0; i < sieve.length; i++)
log.console(c)
$_.stop()
$stop()

View File

@@ -55,4 +55,4 @@ var n = arg[0] || 10
log.console(`Pfannkuchen(${n}) = ${fannkuch(n)}`)
$_.stop()
$stop()

View File

@@ -13,4 +13,4 @@ for (var i in arr) {
log.console(`elapsed: ${time.number()-now}`)
$_.stop()
$stop()

View File

@@ -393,4 +393,4 @@ log.console("");
log.console("---------------------------------------------------------");
log.console("Benchmark complete.\n");
$_.stop()
$stop()

View File

@@ -37,4 +37,4 @@ for (let y = 0; y < h; ++y) {
log.console(text(row, 'b'));
}
$_.stop()
$stop()

View File

@@ -2,11 +2,11 @@ var math = use('math/radians')
var N = 1000000;
var num = 0;
for (var i = 0; i < N; i ++) {
var x = 2 * $_.random();
var y = $_.random();
var x = 2 * $random();
var y = $random();
if (y < math.sine(x * x))
num++;
}
log.console(2 * num / N);
$_.stop()
$stop()

View File

@@ -185,4 +185,4 @@ log.console(` stack_size: ${fn_info.stack_size}`)
log.console(js.disassemble(advance))
log.console(js.disassemble(advance).length)
$_.stop()
$stop()

View File

@@ -49,4 +49,4 @@ function spectralnorm(n) {
log.console(spectralnorm(arg[0]).toFixed(9));
$_.stop()
$stop()

View File

@@ -15,7 +15,7 @@
// Parse command line arguments
if (arg.length != 2) {
log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
$_.stop()
$stop()
}
var lib_name = arg[0];
@@ -172,13 +172,13 @@ var bench = benchmarks.find(b => b.name == scenario_name);
if (!lib) {
log.console('Unknown library:', lib_name);
log.console('Available libraries:', libraries.map(l => l.name).join(', '));
$_.stop()
$stop()
}
if (!bench) {
log.console('Unknown scenario:', scenario_name);
log.console('Available scenarios:', benchmarks.map(b => b.name).join(', '));
$_.stop()
$stop()
}
// Run the benchmark for this library/scenario combination
@@ -201,4 +201,4 @@ var result = {
log.console(result);
$_.stop()
$stop()

View File

@@ -20,25 +20,25 @@ for (var i = 0; i < args.length; i++) {
target = args[++i]
} else {
log.error('-t requires a target')
$_.stop()
$stop()
}
} else if (args[i] == '-p' || args[i] == '--package') {
if (i + 1 < args.length) {
target_package = args[++i]
} else {
log.error('-p requires a package name')
$_.stop()
$stop()
}
} else if (args[i] == '-b' || args[i] == '--buildtype') {
if (i + 1 < args.length) {
buildtype = args[++i]
if (buildtype != 'release' && buildtype != 'debug' && buildtype != 'minsize') {
log.error('Invalid buildtype: ' + buildtype + '. Must be release, debug, or minsize')
$_.stop()
$stop()
}
} else {
log.error('-b requires a buildtype (release, debug, minsize)')
$_.stop()
$stop()
}
} else if (args[i] == '--list-targets') {
log.console('Available targets:')
@@ -46,7 +46,7 @@ for (var i = 0; i < args.length; i++) {
for (var t = 0; t < targets.length; t++) {
log.console(' ' + targets[t])
}
$_.stop()
$stop()
}
}
@@ -59,7 +59,7 @@ if (!target) {
if (target && !build.has_target(target)) {
log.error('Invalid target: ' + target)
log.console('Available targets: ' + build.list_targets().join(', '))
$_.stop()
$stop()
}
var packages = shop.list_packages()
@@ -79,7 +79,7 @@ if (target_package) {
}
} catch (e) {
log.error('Build failed: ' + e)
$_.stop()
$stop()
}
} else {
// Build all packages
@@ -99,4 +99,4 @@ if (target_package) {
log.console(`Build complete: ${success} libraries built${failed > 0 ? `, ${failed} failed` : ''}`)
}
$_.stop()
$stop()

View File

@@ -7,7 +7,7 @@ var build_dir = shop.get_shop_path() + '/build'
if (!fd.is_dir(build_dir)) {
log.console("No build directory found at " + build_dir)
$_.stop()
$stop()
return
}
@@ -23,4 +23,4 @@ try {
log.console("Clean complete!")
$_.stop()
$stop()

View File

@@ -99,14 +99,14 @@ function print_config(obj, prefix = '') {
// Main command handling
if (args.length == 0) {
print_help()
$_.stop()
$stop()
return
}
var config = pkg.load_config()
if (!config) {
log.error("Failed to load cell.toml")
$_.stop()
$stop()
return
}
@@ -128,7 +128,7 @@ switch (command) {
case 'get':
if (args.length < 2) {
log.error("Usage: cell config get <key>")
$_.stop()
$stop()
return
}
var key = args[1]
@@ -148,7 +148,7 @@ switch (command) {
case 'set':
if (args.length < 3) {
log.error("Usage: cell config set <key> <value>")
$_.stop()
$stop()
return
}
var key = args[1]
@@ -164,7 +164,7 @@ switch (command) {
]
if (!valid_system_keys.includes(path[1])) {
log.error("Invalid system key. Valid keys: " + valid_system_keys.join(', '))
$_.stop()
$stop()
return
}
}
@@ -178,7 +178,7 @@ switch (command) {
// Handle actor-specific configuration
if (args.length < 3) {
log.error("Usage: cell config actor <name> <command> [options]")
$_.stop()
$stop()
return
}
@@ -203,7 +203,7 @@ switch (command) {
case 'get':
if (args.length < 4) {
log.error("Usage: cell config actor <name> get <key>")
$_.stop()
$stop()
return
}
var key = args[3]
@@ -220,7 +220,7 @@ switch (command) {
case 'set':
if (args.length < 5) {
log.error("Usage: cell config actor <name> set <key> <value>")
$_.stop()
$stop()
return
}
var key = args[3]
@@ -244,4 +244,4 @@ switch (command) {
print_help()
}
$_.stop()
$stop()

View File

@@ -62,7 +62,7 @@ An actor is a script that **does not return a value**. It runs as an independent
// worker.ce
log.console("Worker started")
$_.on_message = function(msg) {
$on_message = function(msg) {
log.console("Received:", msg)
// Process message...
}
@@ -86,12 +86,12 @@ Reference to the current actor.
log.console($me) // actor reference
```
### $_.stop()
### $stop()
Stop the current actor.
```javascript
$_.stop()
$stop()
```
### $send(actor, message, callback)
@@ -207,7 +207,7 @@ $start(function(worker) {
$delay(function() {
log.console("Shutting down")
$_.stop()
$stop()
}, 10)
```

View File

@@ -17,7 +17,7 @@ Cell is an actor-based scripting language for building concurrent applications.
```javascript
// hello.ce - A simple actor
log.console("Hello, Cell!")
$_.stop()
$stop()
```
```bash

View File

@@ -48,7 +48,7 @@ function get_status() {
}
// Main message receiver
$_.receiver(function(msg) {
$receiver(function(msg) {
switch (msg.type) {
case 'download':
if (state.downloading) {
@@ -86,7 +86,7 @@ $_.receiver(function(msg) {
}
// Schedule the first chunk read
$_.delay(read_next_chunk, 0);
$delay(read_next_chunk, 0);
} catch (e) {
state.error = e.toString();
@@ -167,7 +167,7 @@ function read_next_chunk() {
}
// Schedule next chunk read
$_.delay(read_next_chunk, 0);
$delay(read_next_chunk, 0);
} catch (e) {
// Error during download

View File

@@ -7,7 +7,7 @@ var json = use('json');
var waiting_client = null;
var match_id = 0;
$_.portal(e => {
$portal(e => {
log.console("NAT server: received connection request");
if (!isa(e.actor, actor))

View File

@@ -1,6 +1,6 @@
log.console(`nat client starting`)
$_.contact((actor, reason) => {
$contact((actor, reason) => {
if (actor) {
log.console(`trying to message ${actor}`)
send(actor, {type:"greet"})
@@ -10,10 +10,10 @@ $_.contact((actor, reason) => {
}, {
address: "108.210.60.32", // NAT server's public IP
port: 4000,
actor: $_
actor: $self
})
$_.receiver(e => {
$receiver(e => {
switch(e.type) {
case 'greet':
log.console(`hello!`)

View File

@@ -23,7 +23,7 @@ for (var i = 0; i < args.length; i++) {
log.console("")
log.console("This command ensures that the zip files on disk match what's in")
log.console("the lock file. For local packages, this is a no-op.")
$_.stop()
$stop()
} else if (!args[i].startsWith('-')) {
target_pkg = args[i]
}
@@ -37,7 +37,7 @@ if (target_pkg) {
// Fetch specific package
if (!all_packages.includes(target_pkg)) {
log.error("Package not found: " + target_pkg)
$_.stop()
$stop()
}
packages_to_fetch.push(target_pkg)
} else {
@@ -83,4 +83,4 @@ for (var pkg of packages_to_fetch) {
log.console("")
log.console("Fetch complete: " + text(success_count) + " fetched, " + text(skip_count) + " skipped, " + text(fail_count) + " failed")
$_.stop()
$stop()

View File

@@ -15,7 +15,7 @@ if (command) {
log.error("No help available for command: " + command)
log.console("Run 'cell help' to see available commands.")
}
$_.stop()
$stop()
return
}
@@ -44,4 +44,4 @@ if (stat && stat.isFile) {
log.console("Run 'cell help <command>' for more information on a command.")
}
$_.stop()
$stop()

View File

@@ -6,7 +6,7 @@ var build = use('build')
if (args.length < 1) {
log.console("Usage: cell install <locator>")
$_.stop()
$stop()
return
}
@@ -15,7 +15,7 @@ var locator = args[0]
log.console("Installing " + locator + "...")
if (!shop.update(locator)) {
log.console("Failed to install " + locator)
$_.stop()
$stop()
return
}
@@ -29,4 +29,4 @@ for (var dep of deps) {
build.build_package(locator)
log.console("Installed " + locator)
$_.stop()
$stop()

View File

@@ -1,15 +1,19 @@
(function engine() {
var ACTORDATA = cell.hidden.actorsym
var _cell = globalThis.cell
delete globalThis.cell
var ACTORDATA = _cell.hidden.actorsym
var SYSYM = '__SYSTEM__'
var hidden = cell.hidden
var __cell = globalThis._cell
var hidden = _cell.hidden
var os = hidden.os;
cell.os = null
_cell.os = null
var dylib_ext
cell.id ??= "newguy"
_cell.id ??= "newguy"
switch(os.platform()) {
case 'Windows': dylib_ext = '.dll'; break;
@@ -25,7 +29,6 @@ function use_embed(name) {
return load_internal(`js_${name}_use`)
}
globalThis.use = use_embed
globalThis.meme = function(obj) {
return {
__proto__: obj
@@ -77,9 +80,9 @@ function use_core(path) {
if (fd.is_file(file_path)) {
var script_blob = fd.slurp(file_path)
var script = utf8.decode(script_blob)
var mod = `(function setup_module($_){${script}})`
var mod = `(function setup_module(use){${script}})`
var fn = js.eval('core:' + path, mod)
var result = fn.call(sym);
var result = fn.call(sym, use_core);
use_cache[cache_key] = result;
return result;
}
@@ -88,9 +91,7 @@ function use_core(path) {
return sym;
}
globalThis.use = use_core
var blob = use('blob')
var blob = use_core('blob')
var blob_stone = blob.prototype.stone
var blob_stonep = blob.prototype.stonep;
delete blob.prototype.stone;
@@ -141,16 +142,16 @@ stone.p = function(object)
return _ObjectIsFrozen(object)
}
var actor_mod = use('actor')
var wota = use('wota')
var nota = use('nota')
var actor_mod = use_core('actor')
var wota = use_core('wota')
var nota = use_core('nota')
// Load internal modules for global functions
globalThis.text = use('internal/text')
globalThis.number = use('internal/number')
globalThis.array = use('internal/array')
globalThis.object = use('internal/object')
globalThis.fn = use('internal/fn')
globalThis.text = use_core('internal/text')
globalThis.number = use_core('internal/number')
globalThis.array = use_core('internal/array')
globalThis.object = use_core('internal/object')
globalThis.fn = use_core('internal/fn')
// Global utility functions (use already-captured references)
var _isArray = _ArrayIsArray
@@ -319,7 +320,7 @@ function caller_data(depth = 0)
}
function console_rec(line, file, msg) {
return `[${cell.id.slice(0,5)}] [${file}:${line}]: ${msg}\n`
return `[${_cell.id.slice(0,5)}] [${file}:${line}]: ${msg}\n`
// time: [${time.text("mb d yyyy h:nn:ss")}]
}
@@ -374,9 +375,9 @@ function disrupt(err)
actor_mod.on_exception(disrupt)
cell.args = cell.hidden.init
cell.args ??= {}
cell.id ??= "newguy"
_cell.args = _cell.hidden.init
_cell.args ??= {}
_cell.id ??= "newguy"
function create_actor(desc = {id:guid()}) {
var actor = {}
@@ -384,24 +385,85 @@ function create_actor(desc = {id:guid()}) {
return actor
}
var $_ = create_actor()
var $_ = {}
$_.self = create_actor()
os.use_cache = use_cache
os.global_shop_path = shop_path
os.$_ = $_
var shop = use('shop')
var shop = use_core('shop')
globalThis.use = shop.use
var json = use('json')
var time = use('time')
var json = use_core('json')
var time = use_core('time')
var pronto = use('pronto')
var pronto = use_core('pronto')
globalThis.fallback = pronto.fallback
globalThis.parallel = pronto.parallel
globalThis.race = pronto.race
globalThis.sequence = pronto.sequence
$_.time_limit = function(requestor, seconds)
{
if (!pronto.is_requestor(requestor))
throw new Error('time_limit: first argument must be a requestor');
if (!isa(seconds, number) || seconds <= 0)
throw new Error('time_limit: seconds must be a positive number');
return function time_limit_requestor(callback, value) {
pronto.check_callback(callback, 'time_limit')
var finished = false
var requestor_cancel = null
var timer_cancel = null
function cancel(reason) {
if (finished) return
finished = true
if (timer_cancel) {
timer_cancel()
timer_cancel = null
}
if (requestor_cancel) {
try { pronto.requestor_cancel(reason) } catch (_) {}
requestor_cancel = null
}
}
timer_cancel = $_.delay(function() {
if (finished) return
def reason = make_reason(factory, 'Timeout.', seconds)
if (requestor_cancel) {
try { requestor_cancel(reason) } catch (_) {}
requestor_cancel = null
}
finished = true
callback(null, reason)
}, seconds)
try {
requestor_cancel = requestor(function(val, reason) {
if (finished) return
finished = true
if (timer_cancel) {
timer_cancel()
timer_cancel = null
}
callback(val, reason)
}, value)
} catch (ex) {
cancel(ex)
callback(null, ex)
}
return function(reason) {
if (requestor_cancel) {
try { requestor_cancel(reason) } catch (_) {}
requestor_cancel = null
}
}
}
}
var config = {
ar_timer: 60,
actor_memory:0,
@@ -410,7 +472,7 @@ var config = {
main: true
}
cell.config = config
_cell.config = config
ENETSERVICE = config.net_service
REPLYTIMEOUT = config.reply_timeout
@@ -575,7 +637,7 @@ $_.start = function start(cb, program, ...args) {
if (args.length == 1 && Array.isArray(args[0])) args = args[0]
var startup = {
id,
overling: $_,
overling: $_.self,
root,
arg: args,
program,
@@ -618,14 +680,14 @@ $_.delay = function delay(fn, seconds = 0) {
return function() { actor_mod.removetimer(id) }
}
var enet = use('enet')
var enet = use_core('enet')
// causes this actor to stop when another actor stops.
var couplings = new Set()
$_.couple = function couple(actor) {
if (actor == $_) return // can't couple to self
if (actor == $_.self) return // can't couple to self
couplings.add(actor[ACTORDATA].id)
sys_msg(actor, {kind:'couple', from: $_})
sys_msg(actor, {kind:'couple', from: $_.self})
log.system(`coupled to ${actor}`)
}
@@ -651,7 +713,7 @@ function actor_send(actor, message) {
if (typeof message != 'object') throw new Error('Must send an object record.')
// message to self
if (actor[ACTORDATA].id == cell.id) {
if (actor[ACTORDATA].id == _cell.id) {
if (receive_fn) receive_fn(message.data)
return
}
@@ -742,7 +804,7 @@ globalThis.send = function send(actor, message, reply) {
}
}, REPLYTIMEOUT)
send.reply = id
send.replycc = $_
send.replycc = $_.self
}
// Instead of sending immediately, queue it
@@ -751,10 +813,10 @@ globalThis.send = function send(actor, message, reply) {
stone(send)
if (!cell.args.id) cell.id = guid()
else cell.id = cell.args.id
if (!_cell.args.id) _cell.id = guid()
else _cell.id = _cell.args.id
$_[ACTORDATA].id = cell.id
$_.self[ACTORDATA].id = _cell.id
// Actor's timeslice for processing a single message
function turn(msg)
@@ -767,7 +829,7 @@ function turn(msg)
//log.console(`FIXME: need to get main from config, not just set to true`)
//log.console(`FIXME: remove global access (ie globalThis.use)`)
//log.console(`FIXME: add freeze/unfreeze at this level, so we can do it (but scripts cannot)`)
actor_mod.register_actor(cell.id, turn, true, config.ar_timer)
actor_mod.register_actor(_cell.id, turn, true, config.ar_timer)
if (config.actor_memory)
js.mem_limit(config.actor_memory)
@@ -775,14 +837,16 @@ if (config.actor_memory)
if (config.stack_max)
js.max_stacksize(config.system.stack_max);
overling = cell.args.overling
root = cell.args.root
root ??= $_
overling = _cell.args.overling
$_.overling = overling
root = _cell.args.root
root ??= $_.self
if (overling) {
$_.couple(overling) // auto couple to overling
report_to_overling({type:'greet', actor: $_})
report_to_overling({type:'greet', actor: $_.self})
}
// sys messages are always dispatched immediately
@@ -795,11 +859,11 @@ function sys_msg(actor, msg)
function report_to_overling(msg)
{
if (!overling) return
sys_msg(overling, {kind:'underling', message:msg, from: $_})
sys_msg(overling, {kind:'underling', message:msg, from: $_.self})
}
// Determine the program to run from command line
var program = cell.args.program
var program = _cell.args.program
if (!program) {
log.error('No program specified. Usage: cell <program.ce> [args...]')
@@ -890,21 +954,21 @@ var init_end = time.number()
var load_program_start = time.number()
// Finally, run the program
actor_mod.setname(cell.args.program)
actor_mod.setname(_cell.args.program)
var prog = cell.args.program
var prog = _cell.args.program
var package = use('package')
var package = use_core('package')
var locator = shop.resolve_locator(cell.args.program + ".ce", null)
var locator = shop.resolve_locator(_cell.args.program + ".ce", null)
if (!locator) {
var pkg = package.find_package_dir(cell.args.program + ".ce")
locator = shop.resolve_locator(cell.args.program + ".ce", pkg)
var pkg = package.find_package_dir(_cell.args.program + ".ce")
locator = shop.resolve_locator(_cell.args.program + ".ce", pkg)
}
if (!locator)
throw new Error(`Main program ${cell.args.program} could not be found`)
throw new Error(`Main program ${_cell.args.program} could not be found`)
// Hide JavaScript built-ins - make them inaccessible
// Store references we need internally before deleting
@@ -921,9 +985,9 @@ var _JSON = JSON
// juicing these before Math is gone
use('math/radians')
use('math/cycles')
use('math/degrees')
use_core('math/radians')
use_core('math/cycles')
use_core('math/degrees')
// Delete from globalThis
delete globalThis.Object
@@ -974,7 +1038,26 @@ delete globalThis.RegExp
_ObjectFreeze(globalThis)
$_.clock(_ => {
var val = locator.symbol.call(null, $_, cell.args.arg);
// Get capabilities for the main program
var file_info = shop.file_info ? shop.file_info(locator.path) : null
var inject = shop.script_inject_for ? shop.script_inject_for(file_info) : []
// Build values array for injection
var vals = []
for (var i = 0; i < inject.length; i++) {
var key = inject[i]
if (key && key[0] == '$') key = key.substring(1)
if (key == 'fd') vals.push(fd)
else vals.push($_[key])
}
// Create use function bound to the program's package
var pkg = file_info ? file_info.package : null
var use_fn = function(path) { return shop.use(path, pkg) }
// Call with signature: setup_module(args, use, ...capabilities)
// The script wrapper builds $_ from the injected capabilities for backward compatibility
var val = locator.symbol.call(null, _cell.args.arg, use_fn, ...vals)
if (val)
throw new Error('Program must not return anything');

View File

@@ -1,7 +1,4 @@
/* number.cm - number conversion and math utilities */
var math = use('math/radians')
var _floor = Math.floor
var _ceil = Math.ceil
var _round = Math.round
@@ -9,7 +6,7 @@ var _abs = Math.abs
var _trunc = Math.trunc
var _min = Math.min
var _max = Math.max
var _pow = math.power
var _pow = Math.pow
var _parseInt = parseInt
var _parseFloat = parseFloat

View File

@@ -1,5 +1,4 @@
/* text.cm - text conversion and formatting utilities */
var blob = use('blob')
var utf8 = use('utf8')

18
link.ce
View File

@@ -26,7 +26,7 @@ if (args.length < 1) {
log.console(" clear Remove all links")
log.console(" <path> Link the package in <path> to that path")
log.console(" <package> <target> Link <package> to <target> (path or package)")
$_.stop()
$stop()
return
}
@@ -55,7 +55,7 @@ if (cmd == 'list') {
} else if (cmd == 'delete' || cmd == 'rm') {
if (args.length < 2) {
log.console("Usage: link delete <package>")
$_.stop()
$stop()
return
}
@@ -96,7 +96,7 @@ if (cmd == 'list') {
if (!arg1) {
log.console("Error: target or package required")
$_.stop()
$stop()
return
}
@@ -136,7 +136,7 @@ if (cmd == 'list') {
if (!fd.is_file(toml_path)) {
log.console("Error: No cell.toml found at " + target)
log.console("For linking to another package, use: link <package> <target>")
$_.stop()
$stop()
return
}
@@ -147,12 +147,12 @@ if (cmd == 'list') {
pkg_name = content.package
} else {
log.console("Error: cell.toml at " + target + " does not define 'package'")
$_.stop()
$stop()
return
}
} catch (e) {
log.console("Error reading cell.toml: " + e)
$_.stop()
$stop()
return
}
}
@@ -161,7 +161,7 @@ if (cmd == 'list') {
if (target.startsWith('/')) {
if (!fd.is_file(target + '/cell.toml')) {
log.console("Error: " + target + " is not a valid package (no cell.toml)")
$_.stop()
$stop()
return
}
}
@@ -171,9 +171,9 @@ if (cmd == 'list') {
link.add(pkg_name, target, shop)
} catch (e) {
log.console("Error: " + e.message)
$_.stop()
$stop()
return
}
}
$_.stop()
$stop()

View File

@@ -17,7 +17,7 @@ if (args && args.length > 0) {
} else if (args[0] == 'package') {
if (args.length < 2) {
log.console("Usage: cell list package <name>")
$_.stop()
$stop()
return
}
mode = 'package'
@@ -28,7 +28,7 @@ if (args && args.length > 0) {
log.console(" cell list all : list all recursive packages")
log.console(" cell list package <name>: list dependencies of <name>")
log.console(" cell list shop : list all packages in shop")
$_.stop()
$stop()
return
}
}
@@ -82,4 +82,4 @@ function print_deps(ctx) {
}
}
$_.stop()
$stop()

2
ls.ce
View File

@@ -31,4 +31,4 @@ if (programs.length == 0) {
}
}
$_.stop()
$stop()

20
pack.ce
View File

@@ -23,7 +23,7 @@ if (args.length < 1) {
log.error(' -b, --buildtype <type> Build type: release, debug, minsize (default: release)')
log.error('')
log.error('Available targets: ' + build.list_targets().join(', '))
$_.stop()
$stop()
return
}
@@ -35,25 +35,25 @@ for (var i = 1; i < args.length; i++) {
target = args[++i]
} else {
log.error('-t requires a target')
$_.stop()
$stop()
}
} else if (args[i] == '-o' || args[i] == '--output') {
if (i + 1 < args.length) {
output_name = args[++i]
} else {
log.error('-o requires an output name')
$_.stop()
$stop()
}
} else if (args[i] == '-b' || args[i] == '--buildtype') {
if (i + 1 < args.length) {
buildtype = args[++i]
if (buildtype != 'release' && buildtype != 'debug' && buildtype != 'minsize') {
log.error('Invalid buildtype: ' + buildtype + '. Must be release, debug, or minsize')
$_.stop()
$stop()
}
} else {
log.error('-b requires a buildtype (release, debug, minsize)')
$_.stop()
$stop()
}
} else if (args[i] == '-h' || args[i] == '--help') {
log.console('Usage: cell pack <package> [options]')
@@ -64,10 +64,10 @@ for (var i = 1; i < args.length; i++) {
log.console(' -b, --buildtype <type> Build type: release, debug, minsize (default: release)')
log.console('')
log.console('Available targets: ' + build.list_targets().join(', '))
$_.stop()
$stop()
} else {
log.error('Unknown option: ' + args[i])
$_.stop()
$stop()
}
}
@@ -80,7 +80,7 @@ if (!target) {
if (target && !build.has_target(target)) {
log.error('Invalid target: ' + target)
log.console('Available targets: ' + build.list_targets().join(', '))
$_.stop()
$stop()
}
// Prepare packages: core + dependencies + target package
@@ -117,7 +117,7 @@ try {
} catch (e) {
log.error('Build failed: ')
log.error(e)
$_.stop()
$stop()
}
$_.stop()
$stop()

View File

@@ -3,9 +3,6 @@
// Based on Douglas Crockford's parseq, adapted for Cell.
// Time is in seconds.
var os = use('os')
var $_ = os.$_
function make_reason(factory, excuse, evidence) {
def reason = new Error(`pronto.${factory}${excuse ? ': ' + excuse : ''}`)
reason.evidence = evidence
@@ -303,69 +300,6 @@ function sequence(requestor_array) {
}
}
// time_limit(requestor, seconds)
// Wraps a requestor with a time limit.
function time_limit(requestor, seconds) {
def factory = 'time_limit'
if (!is_requestor(requestor))
throw make_reason(factory, 'Not a requestor.', requestor)
if (typeof seconds != 'number' || seconds <= 0)
throw make_reason(factory, 'Bad time limit.', seconds)
return function time_limit_requestor(callback, value) {
check_callback(callback, factory)
let finished = false
let requestor_cancel = null
let timer_cancel = null
function cancel(reason) {
if (finished) return
finished = true
if (timer_cancel) {
timer_cancel()
timer_cancel = null
}
if (requestor_cancel) {
try { requestor_cancel(reason) } catch (_) {}
requestor_cancel = null
}
}
timer_cancel = $_.delay(function() {
if (finished) return
def reason = make_reason(factory, 'Timeout.', seconds)
if (requestor_cancel) {
try { requestor_cancel(reason) } catch (_) {}
requestor_cancel = null
}
finished = true
callback(null, reason)
}, seconds)
try {
requestor_cancel = requestor(function(val, reason) {
if (finished) return
finished = true
if (timer_cancel) {
timer_cancel()
timer_cancel = null
}
callback(val, reason)
}, value)
} catch (ex) {
cancel(ex)
callback(null, ex)
}
return function(reason) {
if (requestor_cancel) {
try { requestor_cancel(reason) } catch (_) {}
requestor_cancel = null
}
}
}
}
// requestorize(unary)
// Converts a unary function into a requestor.
function requestorize(unary) {
@@ -421,7 +355,8 @@ return {
parallel,
race,
sequence,
time_limit,
requestorize,
objectify
objectify,
is_requestor,
check_callback
}

View File

@@ -143,4 +143,4 @@ if (typeof arg == 'undefined' || arg.length < 1) {
}
}
$_.stop()
$stop()

View File

@@ -4,10 +4,10 @@ var shop = use('shop')
if (args.length < 1) {
log.console("Usage: cell remove <alias|path>")
$_.stop()
$stop()
return
}
shop.remove(args[0])
$_.stop()
$stop()

101
shop.cm
View File

@@ -180,11 +180,12 @@ Shop.file_info = function(file) {
if (pkg_dir) {
info.package = abs_path_to_package(pkg_dir)
info.name = file.substring(pkg_dir.length + 1)
if (info.is_actor)
info.name = info.path.substring(0, info.path.length - ACTOR_EXT.length)
info.name = file.substring(pkg_dir.length + 1, file.length - ACTOR_EXT.length)
else if (info.is_module)
info.name = info.path.substring(0, info.path.length - MOD_EXT.length)
info.name = file.substring(pkg_dir.length + 1, file.length - MOD_EXT.length)
else
info.name = file.substring(pkg_dir.length + 1)
}
return info
@@ -325,11 +326,73 @@ Shop.extract_commit_hash = function(pkg, response) {
var open_dls = {}
// Default capabilities injected into scripts
// These map to $_ properties in engine.cm
var SHOP_DEFAULT_INJECT = ['$self', '$overling', '$clock', '$delay', '$start', '$receiver', '$contact', '$portal', '$time_limit', '$couple', '$stop', '$unneeded', '$connection', '$fd']
function strip_dollar(name) {
if (name && name[0] == '$') return name.substring(1)
return name
}
// Decide what a given module is allowed to see.
// This is the capability gate - tweak as needed.
Shop.script_inject_for = function(file_info) {
if (!file_info) return []
// For now, grant everything to all scripts
// Later this can be tuned per package/script
return array(SHOP_DEFAULT_INJECT)
}
// Get capabilities for a script path (public API)
Shop.get_script_capabilities = function(path) {
var file_info = Shop.file_info(path)
return Shop.script_inject_for(file_info)
}
function inject_params(inject) {
if (!inject || !inject.length) return ''
return ', ' + inject.join(', ')
}
function inject_values(inject) {
var vals = []
for (var i = 0; i < inject.length; i++) {
var key = strip_dollar(inject[i])
if (key == 'fd') vals.push(fd)
else vals.push($_[key])
}
return vals
}
// Build the use function for a specific package context
function make_use_fn_code(pkg_arg) {
return `function(path) { return globalThis.use(path, ${pkg_arg}); }`
}
// for script forms, path is the canonical path of the module
var script_form = function(path, script, pkg) {
var script_form = function(path, script, pkg, inject) {
var pkg_arg = pkg ? `'${pkg}'` : 'null'
var relative_use_fn = `def PACKAGE = ${pkg_arg}; def use = function(path) { return globalThis.use(path, ${pkg_arg});}`
var fn = `(function setup_module($_, args){ def arg = args; ${relative_use_fn}; ${script}})`
var params = inject_params(inject)
// Build $_ object from injected capabilities for backward compatibility
var build_compat = ''
if (inject && inject.length) {
var compat_props = []
for (var i = 0; i < inject.length; i++) {
var name = inject[i]
var key = name
if (key && key[0] == '$') key = key.substring(1)
compat_props.push(key + ': ' + name)
}
build_compat = 'var $_ = {' + compat_props.join(', ') + '};'
}
// use is passed as a parameter, not on globalThis for the script
// $fd is injected as a capability, but we still allow use('fd') for now
// $_ is built from injected capabilities for backward compatibility
var fn = `(function setup_module(args, use${params}){ def arg = args; def PACKAGE = ${pkg_arg}; ${build_compat} ${script}})`
return fn
}
@@ -340,8 +403,9 @@ function resolve_mod_fn(path, pkg) {
var file_info = Shop.file_info(path)
var file_pkg = file_info.package
var inject = Shop.script_inject_for(file_info)
var content = text(fd.slurp(path))
var script = script_form(path, content, file_pkg);
var script = script_form(path, content, file_pkg, inject);
var obj = pull_from_cache(utf8.encode(script))
if (obj) {
@@ -570,7 +634,9 @@ function resolve_module_info(path, package_context) {
// This ensures linked packages resolve to the same cache entry
// whether accessed via symlink or directly
var cache_key
if (mod_resolve.scope < 900 && mod_resolve.path) {
if (mod_resolve.scope == SCOPE_CORE) {
cache_key = 'core/' + path
} else if (mod_resolve.scope < 900 && mod_resolve.path) {
// Use realpath to resolve symlinks and get the actual file location
var real_path = fd.realpath(mod_resolve.path)
if (real_path) {
@@ -628,6 +694,13 @@ Shop.is_loaded = function(path, package_context) {
return use_cache[cache_key] != null
}
// Create a use function bound to a specific package context
function make_use_fn(pkg) {
return function(path) {
return Shop.use(path, pkg)
}
}
function execute_module(info)
{
var c_resolve = info.c_resolve
@@ -640,7 +713,17 @@ function execute_module(info)
if (c_resolve.scope < 900) {
context = c_resolve.symbol(null, $_)
}
used = mod_resolve.symbol.call(context, $_)
// Get file info to determine inject list
var file_info = Shop.file_info(mod_resolve.path)
var inject = Shop.script_inject_for(file_info)
var vals = inject_values(inject)
var pkg = file_info.package
var use_fn = make_use_fn(pkg)
// Call with signature: setup_module(args, use, ...capabilities)
// args is null for module loading
used = mod_resolve.symbol.call(context, null, use_fn, ...vals)
} else if (c_resolve.scope < 900) {
// C only
used = c_resolve.symbol(null, $_)

View File

@@ -146,7 +146,7 @@ void script_startup(cell_rt *prt)
JS_SetPropertyStr(js, cell, "hidden", hidden_fn);
JS_SetPropertyStr(js, hidden_fn, "os", js_os_use(js));
const char actorsym_script[] = "var sym = Symbol(`actordata`); sym;";
const char actorsym_script[] = "Symbol('actordata');";
JSValue actorsym = JS_Eval(js, actorsym_script, sizeof(actorsym_script)-1, "internal", 0);
JS_SetPropertyStr(js, hidden_fn, "actorsym", actorsym);

20
test.ce
View File

@@ -18,7 +18,7 @@ if (args.length > 0) {
if (args[0] == 'package') {
if (args.length < 2) {
log.console(`Usage: cell test package <name>`)
$_.stop()
$stop()
return
}
var name = args[1]
@@ -28,7 +28,7 @@ if (args.length > 0) {
log.console(`Testing package: ${resolved.alias} (${resolved.pkg})`)
} else {
log.console(`Package not found: ${name}`)
$_.stop()
$stop()
return
}
} else if (args[0] == 'all') {
@@ -36,7 +36,7 @@ if (args.length > 0) {
log.console(`Testing all packages...`)
} else {
log.console(`Usage: cell test [package <name> | all]`)
$_.stop()
$stop()
return
}
}
@@ -95,9 +95,9 @@ function spawn_actor_test(test_info) {
try {
// Spawn the actor test - it should send back results
// The actor receives $_.parent which it can use to send results
// The actor receives $parent which it can use to send results
var actor_path = test_info.path.substring(0, test_info.path.length - 3) // remove .ce
entry.actor = $_.start(actor_path)
entry.actor = $start(actor_path)
pending_actor_tests.push(entry)
} catch (e) {
entry.status = "failed"
@@ -356,7 +356,7 @@ function check_timeouts() {
}
if (pending_actor_tests.length > 0) {
$_.delay(check_timeouts, 1000)
$delay(check_timeouts, 1000)
}
check_completion()
}
@@ -424,7 +424,7 @@ function finalize_results() {
log.console(`Tests: ${totals.passed} passed, ${totals.failed} failed, ${totals.total} total`)
generate_reports(totals)
$_.stop()
$stop()
}
// If no actor tests, finalize immediately
@@ -441,7 +441,7 @@ if (all_actor_tests.length == 0) {
log.console(`Tests: ${totals.passed} passed, ${totals.failed} failed, ${totals.total} total`)
} else {
// Start timeout checker
$_.delay(check_timeouts, 1000)
$delay(check_timeouts, 1000)
}
@@ -533,10 +533,10 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed}
// If no actor tests, generate reports and stop immediately
if (all_actor_tests.length == 0) {
generate_reports(totals)
$_.stop()
$stop()
} else {
// Set up portal to receive messages from actor tests
$_.portal(function(msg) {
$portal(function(msg) {
handle_actor_message(msg)
})
}

View File

@@ -1,6 +1,6 @@
return {
test_clock: function() {
// Original test verified $_.clock callbacks.
// Original test verified $clock callbacks.
// This cannot be easily tested in a synchronous function return.
// We assume the runtime works.
var i = 0

View File

@@ -1,4 +1,4 @@
var parseq = use('parseq', $_.delay)
var parseq = use('parseq', $delay)
var time = use('time')
return {
@@ -9,14 +9,14 @@ return {
function load_comment_from_api_requestor(id) {
return function(cb) {
return $_.delay(() => cb({ id, title: `Comment #${id}` }), 0.5)
// returning the $_.delay return lets them be cancelled up the chain
return $delay(() => cb({ id, title: `Comment #${id}` }), 0.5)
// returning the $delay return lets them be cancelled up the chain
}
}
$_.receiver(tree => {
$receiver(tree => {
var child_reqs = tree.children.map(child => cb => {
$_.start(e => send(e.actor, child, cb), "tests/comments")
$start(e => send(e.actor, child, cb), "tests/comments")
})
var job = parseq.par_all({

View File

@@ -1,16 +1,16 @@
var parseq = use('parseq', $_.delay)
var parseq = use('parseq', $delay)
var time = use('time')
function load_comment_from_api_requestor(id) {
return function(cb) {
return $_.delay(() => cb({ id, title: `Comment #${id}` }), 0.5)
// returning the $_.delay return lets them be cancelled up the chain
return $delay(() => cb({ id, title: `Comment #${id}` }), 0.5)
// returning the $delay return lets them be cancelled up the chain
}
}
$_.receiver(tree => {
$receiver(tree => {
var child_reqs = tree.children.map(child => cb => {
$_.start(e => send(e.actor, child, cb), "tests/comments") // Note: recursively calls itself? Original used "tests/comments"
$start(e => send(e.actor, child, cb), "tests/comments") // Note: recursively calls itself? Original used "tests/comments"
// We should probably change this to "tests/comments_actor" if it's recursive
})

View File

@@ -12,7 +12,7 @@ return {
log.console(`Did not get an actor: ${reason}`)
}
$_.contact(contact_fn,
$contact(contact_fn,
{
address: "localhost",
port: 5678,

View File

@@ -1,7 +1,7 @@
return {
test_couple: function() {
$_.start(e => {
$_.couple(e.actor)
$start(e => {
$couple(e.actor)
}, "tests/delay_actor")
}
}

View File

@@ -4,9 +4,9 @@ function loop()
count++;
log.console(`loop ${count}`);
if (count < 60)
$_.delay(loop, 0.01);
$delay(loop, 0.01);
else
$_.stop()
$stop()
}
$_.delay(loop,0.01)
$delay(loop,0.01)

View File

@@ -4,7 +4,7 @@ var time = use('time')
return {
test_guid: function() {
var st = time.number()
var guid = new blob(256, $_.random_fit)
var guid = new blob(256, $random_fit)
stone(guid)
var btime = time.number()-st
st = time.number()

View File

@@ -7,7 +7,7 @@ return {
var host = 'google.com'
var path = '/'
$_.start(e => {
$start(e => {
send(e.actor, { op: 'get', domain: host, port: 80}, addrs => {
log.console(addrs[0])
})

View File

@@ -1,14 +1,14 @@
$_.start(e => {
$start(e => {
log.console(`got message from hanger: ${e.type}`)
if (e.type == 'greet')
$_.delay(_ => {
$delay(_ => {
log.console(`sending stop message to hanger`)
$_.stop(e.actor)
$stop(e.actor)
}, 1)
if (e.type == 'disrupt') {
log.console(`underling successfully killed.`)
send($_.parent, { type: "test_result", passed: true })
$_.stop()
send($parent, { type: "test_result", passed: true })
$stop()
}
}, 'tests/hang_actor')

View File

@@ -15,14 +15,14 @@ return {
// Spawn several underlings
for (var i = 0; i < targetCount; i++) {
$_.start(function(greet) {
$start(function(greet) {
underlingCount++;
log.console("Underling spawned: " + underlingCount);
}, "tests/underling_actor", ["test" + i]);
}
// We can't easily wait here without a loop that yields, but this is a single threaded JS env usually.
// If $_.delay is async, we return immediately.
// If $delay is async, we return immediately.
log.console("Spawned " + targetCount + " underlings (async)");
}

View File

@@ -29,9 +29,9 @@ return {
// The original 'comments.ce' was an actor script.
// We should probably restore 'comments.ce' as 'comments_actor.ce' for this test to work if it relies on spawning it.
// But 'comments.cm' (the new test file) also has the logic.
// We need an actor file for $_.start to work with.
// We need an actor file for $start to work with.
$_.start(e => {
$start(e => {
if (actor) return
actor = e.actor
send(actor, tree, (result, reason) => {

View File

@@ -1,6 +1,6 @@
return {
test_portal: function() {
// Starts the portal actor
$_.start(e => {}, "tests/portal_actor")
$start(e => {}, "tests/portal_actor")
}
}

View File

@@ -1,14 +1,14 @@
var password = "abc123"
$_.portal(e => {
$portal(e => {
if (e.password != password)
send(e, {reason:"Password does not match."});
else
send(e, $_)
send(e, $self)
}, 5678);
$_.receiver(e => {
$receiver(e => {
log.console(`Got message: ${e}`)
send(e, {greet: "Hello back!"})
$_.delay(_ => $_.stop(), 0.2)
$delay(_ => $stop(), 0.2)
})

View File

@@ -7,7 +7,7 @@ function make_requestor(name, delay_seconds, should_succeed) {
return function(callback, value) {
log.console(`Starting ${name} with value: ${value}`)
if (delay_seconds > 0) {
$_.delay(function() {
$delay(function() {
if (should_succeed) {
log.console(`${name} succeeded with: ${value + 1}`)
callback(value + 1)

View File

@@ -1 +1 @@
$_.start(e => {}, "tests/reply_actor")
$start(e => {}, "tests/reply_actor")

View File

@@ -1,4 +1,4 @@
$_.receiver(e => {
$receiver(e => {
log.console(`Got a message: ${e}`)
send(e, {

View File

@@ -1,9 +1,9 @@
return {
test_send: function() {
$_.start(e => {
$start(e => {
send(e.actor, { message: "Hello! Good to go?" }, msg => {
log.console(`Original sender got message back: ${msg}. Stopping!`)
// $_.stop() // Removed
// $stop() // Removed
})
}, "tests/reply_actor")
}

View File

@@ -1,6 +1,6 @@
return {
test_stop: function() {
log.console(`About to stop.`)
// $_.stop() // Removed
// $stop() // Removed
}
}

View File

@@ -1,7 +1,7 @@
var cmds = {
stop: $_.stop,
stop: $stop,
disrupt: _ => {
$_.delay(_ => { throw new Error() }, 0.5)
$delay(_ => { throw new Error() }, 0.5)
}
}

View File

@@ -1,5 +1,5 @@
return {
test_unneeded: function() {
$_.start(e => {}, "tests/unneeded_actor")
$start(e => {}, "tests/unneeded_actor")
}
}

View File

@@ -1,4 +1,4 @@
$_.unneeded(_ => {
$unneeded(_ => {
log.console("Unneded function fired.");
$_.start(null, "tests/unneeded_actor")
$start(null, "tests/unneeded_actor")
}, 1);

View File

@@ -23,7 +23,7 @@ for (var i = 0; i < args.length; i++) {
log.console("")
log.console("This command checks for updates to all packages and downloads")
log.console("new versions. For local packages, ensures the symlink is correct.")
$_.stop()
$stop()
} else if (!args[i].startsWith('-')) {
target_pkg = args[i]
}
@@ -74,4 +74,4 @@ if (target_pkg) {
}
}
$_.stop()
$stop()

View File

@@ -34,4 +34,4 @@ if (cmd == 'link') {
}
}
$_.stop()
$stop()

View File

@@ -1,3 +1,2 @@
var shop = use('shop')
log.console("0.1.0")
$_.stop()
$stop()

4
why.ce
View File

@@ -3,7 +3,7 @@ var pkg = use('package')
if (!args || args.length < 1) {
log.console("Usage: cell why <package>")
$_.stop()
$stop()
return
}
@@ -106,4 +106,4 @@ if (!found) {
log.console("Package '" + target + "' not found in dependency tree.")
}
$_.stop()
$stop()