monome / norns

norns is many sound instruments.
http://monome.org
GNU General Public License v3.0
630 stars 147 forks source link

crow output action assignments use quotes #890

Closed tehn closed 4 years ago

tehn commented 5 years ago

looking for a way to make the action syntax the same for native crow (druid) and norns crow

natively on crow (via druid/etc) you can set action commands like this: output[1].action = lfo(1,4)

right now the norns script

output[1].action = "lfo(1,4)"

sends output[1].action = lfo(1,4) via serial to crow. AFAIK there is no way to just capture the right side of the equals sign. here's where it happens: https://github.com/monome/norns/blob/master/lua/core/crow.lua#L90

i do not think this is not worth solving if it requires some sort of C level hack

catfact commented 5 years ago

yea, i don't think there's a clean way to do this exactly. (maybe with the debug library but not sure.)

following up from slack, at present the main idea is to define lfo() and the other "action functions" globally so that they return strings. to also keep these out of the global environment would require executing scripts in a sandboxed environment

rough mockup:

crow.lua

crow = {}

crow.send = function(what) print(what) end

crow.add_sandbox_globals = function(env)
   env.lfo = function(i,v) return 'lfo('..i..','..v..')' end
   env.volts = function(i,v) return 'volts('..i..','..v..')' end
   return env
end

crow.output = {}

crow.output.__newindex = function(self, key, value)
   print('newindex('..key..', '..value..')')

   if key == 'action' then
      -- "value" should be a string returned from `crow.env` function
      -- but good idea to check type here..
      if type(value) == 'string' then
     crow.send("output = "..value)
      else
     print('error...')
      end
   end
end

setmetatable(crow.output, crow.output)

crow.run_script = function(path)
   print('crow.run_script('..path..')')
   -- make a new environment that inherits from the global env
   local env = {}
   setmetatable(env, {__index = _G})

   -- add our "fake globals"
   env = crow.add_sandbox_globals(env)

   -- attempt to compile with new environment
   local fn = loadfile(path, 'bt', env)
   if fn == nil then print('error...')
   else fn() end
end

return crow

crow_script_launcher.lua

local crow = dofile('crow.lua')
print('script launcher')
crow.run_script('crow_script.lua')

-- ASL functions don't appear in global env:
print("global 'lfo:'", lfo) -- result: nil

crow_script.lua

local crow = dofile('crow.lua')
print('running script')
crow.output.action = lfo(1, 12)
catfact commented 5 years ago

in the case of the norns REPL, we explicitly handle those code chunks at a pretty high level; ultimately the call is here: https://github.com/monome/norns/blob/master/matron/src/lua_eval.c#L297

triggered from here (i can't think of any other place that uses lua_eval besides handling lua chunk events from the matron event loop): https://github.com/monome/norns/blob/master/matron/src/weaver.c#L61

and we could easily switch the environment at this stage - it's a "C level hack" but quite simple.

alternatively, could provide global functions like start_crow_environment and end_crow_environment() or whatever.

mimetaur commented 5 years ago

Saw someone get tripped up by this syntax on lines

pq commented 5 years ago

to also keep these out of the global environment would require executing scripts in a sandboxed environment

iirc we're unloading script contributed globals on script cleanup (or maybe it's in initialization, i can't remember). anyway, i think that makes things even simpler.

if folks like this approach, i'm happy to throw a proposal together.

pq commented 5 years ago

needless to say, yeah @mimetaur: agreed, this is super confusing!

catfact commented 5 years ago

yeah, all my code and blather above can be boiled down to:

downside: the crow environment wouldn't be used automatically in the maiden REPL. (maybe a dumb idea, but something like begin_crow_mode() and end_crow_mode() could switch the evaluation context of REPL chunks.)

catfact commented 5 years ago

oh, right. this actually won't work for cases where you can actually include literals inside functions inside ASL arguments. there is probably no way to do this that isn't pretty insane.