Skip to content

Instantly share code, notes, and snippets.

@bpj
Created June 1, 2020 14:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bpj/091b7ad514e7d17a83fa8b2a4c669579 to your computer and use it in GitHub Desktop.
Save bpj/091b7ad514e7d17a83fa8b2a4c669579 to your computer and use it in GitHub Desktop.
-- DWIM selective table item copying for Moonscript (and Lua)
import_select = (target, source, ...) ->
names = {...}
-- allow passing a table of names,
-- possibly with renames
if 1 == #names and 'table' == type names[1]
names = names[1]
-- map keys are "new" names
-- map vals are "old" names
for tgt, src in pairs names
-- array items are not renamed
if 'number' == type tgt
tgt = tostring(src)\gsub "^[%+%-]+", ""
else
tgt = tostring tgt
if 'table' != type src
src = {src}
for _src in *src
_src = tostring _src
flags, _name = _src\match '^([%+%-]*)(.+)$'
_src = _name or
error "Invalid source name: #{_src}", 2
keep = flags\match "%+"
lax = flags\match "%-"
-- an `*` in tgt is replaced with _src
_tgt = tgt\gsub '%*', _src
if keep and nil != target[_tgt]
continue
if nil == source[_src]
if lax
continue
else
error "Field '#{_src}' (for '#{_tgt}') not found", 2
target[_tgt] = source[_src]
return target
-- f = import_select {}, table, 'pack', 'unpack', 'sort'
-- import_select f, string, {
-- 'match', 'gsub',
-- fmt: 'format',
-- 's_*': {'pack', 'unpack'},
-- }
-- import_select f, require"moon", {
-- pprint: 'p',
-- 'moon_*': 'type',
-- }
-- import_select f, math, { 'm_*': {'floor', 'type'}}
-- -- This won't overwritev(un)pack from table
-- -- nor error because string.title doesn't exist!
-- import_select f, string, '+pack', '+unpack', '-title'
-- moon = require"moon"
-- moon.p f
-- moon.p {
-- pack: (f.pack == table.pack),
-- unpack: (f.unpack == table.unpack),
-- sort: (f.sort == table.sort),
-- match: (f.match == string.match),
-- gsub: (f.gsub == string.gsub),
-- fmt: (f.fmt == string.format),
-- s_pack: (f.s_pack == string.pack),
-- s_unpack: (f.s_unpack == string.unpack),
-- pprint: (f.pprint == moon.p),
-- moon_type: (f.moon_type == moon.type),
-- m_floor: (f.m_floor == math.floor),
-- m_type: (f.m_type == math.type),
-- }
-- -- Don't do this at work!
-- -- Not at home either!
-- _ENV = import_select(
-- import_select(
-- {k,v for k,v in pairs _ENV},
-- table, {
-- 't_*': {'pack', 'unpack', 'sort', 'insert'},
-- }
-- )
-- string, {
-- 's_*': {
-- 'match','gsub', 'gmatch','sub', 'rep',
-- },
-- sprintf: 'format',
-- }
-- )
-- g = {k,true for k in pairs _G}
-- for name in *[k for k in pairs _ENV]
-- unless g[name]
-- print name
-- print "xxx" == s_rep "x", 3
-- print "(foo)" == sprintf "(%s)", "foo"
@bpj
Copy link
Author

bpj commented Jun 1, 2020

DWIM selective table item copying for Moonscript (and Lua)

import_select(target, source, {name, ..., target_name: source_name, ...}|name, ...)

Import selected names and their values from the table source into the table target. Returns target so you can pass an anonymous table as target and assign the return value to a variable.

If only three arguments are passed and argument #3 is a table, fields in that table with a numeric key are taken as simple imports where both the source name and the target name are equal to the value. Otherwise the key is taken to be the target name and the value is taken to be the source name. Thus these three invocations are equivalent:

tb = import_select {}, table, 'pack', 'unpack', 'sort'

tb = import_select {}, table, {'pack', 'unpack', 'sort'}

tb = import_select {}, table, {
      pack: 'pack',
      unpack: 'unpack',
      sort: 'sort',
  }

If the target name contains any * (asterisk/star)character the source name will be substituted for it (or them!), so that these two invocations are equivalent:

import_select fun, math, {m_type: 'type'}

import_select fun, math, {'m_*': 'type'}

Additionally the "source name" may itself be an array of names. This is only really useful when the key contains a star, since else the imports will overwrite each other, but these three invocations are equivalent:

import_select fun, string, { 's_*': {'pack', 'unpack'}}

import_select fun, string, {
    's_*': 'pack',
    's_*': 'unpack',
  }

import_select fun, string, {
    's_pack': 'pack',
    's_unpack': 'unpack',
  }

Naturally you can mix all these in a single table:

import_select fun, string, {
    'match', 'gsub',
    imatch: 'gmatch',
    '*str': 'sub', -- as much to type, I know!
    '*_str': {'pack', 'unpack'},
  }

An error is thrown if the name doesn't exist in the source table, i.e. if nil == source[source_name], except if the source name is prefixed with a - (minus) character (and if so make sure you typed the rest correctly!), and an existing field in the import table is not overwritten if the source name starts with a + (plus) character:

char = import_select(
  import_select(
    {},
    ( utf8 or {} ), {
      cpat: '-charpattern',
      code: '-codepoint',
    }
  ),
  string, { code: '+byte' },
)
char.cpat or= '.'

If you like you can do shenanigans with _ENV so that you can type the import names as bare names, but remember I didn't tell you to do so!

_ENV = import_select(
  import_select(
    {k,v for k,v in pairs _ENV},
    table, {
        't_*': {'pack', 'unpack', 'sort', 'insert'},
      }
  )
  string, {
      's_*': {
          'match','gsub', 'gmatch','sub', 'rep',
        },
      sprintf: 'format',
    }
)

Last but not least the type of the imports are not checked in any way. Did you notice that a string, charpattern, was imported from utf8 above?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment