aboutsummaryrefslogtreecommitdiffstats
path: root/lib/duct_tape.server.moon
blob: b019ac868c7f5387c916e051ac3e9dbeac7331b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package.noompath = './?.moon;./?/init.moon'

export __FNDEF_NOOM_CACHE
__FNDEF_NOOM_CACHE = setmetatable {}, __mode: 'k'

assert MODE == 'SERVER', "duct_tape only works on the server"

compile = require 'moonscript.compile'
parse = require 'moonscript.parse'

import smart_node, build from require 'moonscript.types'
import dirsep from require 'moonscript.base'
import get_options, unpack from require 'moonscript.util'

line_tables = require 'moonscript.line_tables'
lua = :loadstring, :load

local *

escape_str = (str) -> (str\gsub('\\', '\\\\')\gsub '"', '\\"'), nil

deep_clone = (value) ->
  if 'table' == type value
    { deep_clone(k), deep_clone(v) for k,v in pairs value }
  else -- number, string, boolean, etc
    value

transform_fndef = (fndef, parent, i) ->
  -- compile the function to Lua separately
  code, ltable, pos = compile.tree { deep_clone fndef }
  if not code
    return compile.format_error(ltable, pos, text)

  code = "(function() #{code} end)()"

  -- assign the lua code to __FNDEF_NOOM_CACHE[function] so it can be retrieved by `compile`
  fn = -> { 'ref', 'fn' }
  cache = build.chain {
    base: '__FNDEF_NOOM_CACHE',
    { 'index', fn! }
  }

  parent[i] = build.do {
    { 'declare_with_shadows', { 'fn' } }
    build.assign_one fn!, fndef
    build.assign_one cache, { 'string', '"', escape_str code }
    -- build.assign_one cache, { 'string', '[[', code }
    fn!
  }

  nil

find_fndefs = (node, p, i) ->
  if node[1] == "fndef"
    transform_fndef node, p, i
  else
    for i = 1,#node
      v = node[i]
      find_fndefs v, node, i if 'table' == type v

to_lua = (text, options={}) ->
  if "string" != type text
    t = type text
    return nil, "expecting string (got ".. t ..")"

  tree, err = parse.string text
  if not tree
    return nil, err

  find_fndefs tree

  code, ltable, pos = compile.tree tree, options
  if not code
    return nil, compile.format_error(ltable, pos, text)

  code, ltable

noom_loader = (name, ...) ->
  name_path = name\gsub "%.", dirsep

  local file, file_path
  for path in package.noompath\gmatch "[^;]+"
    file_path = path\gsub "?", name_path
    file = io.open file_path
    break if file

  if file
    text = file\read "*a"
    file\close!
    res, err = loadstring text, "@#{file_path}"
    if not res
        error file_path .. ": " .. err

    return res

  return nil, "Could not find noom file"

loadstring = (...) ->
  options, str, chunk_name, mode, env = get_options ...
  chunk_name or= "=(noom.loadstring)"

  code, ltable_or_err = to_lua str, options
  unless code
    return nil, ltable_or_err

  line_tables[chunk_name] = ltable_or_err if chunk_name

  -- the unpack prevents us from passing nil
  (lua.loadstring or lua.load) code, chunk_name, unpack { mode, env }

do
  local compile
  insert_loader = (pos=2) ->
    -- if not package.moonpath
    --   package.moonpath = create_moonpath package.path

    loaders = package.loaders or package.searchers
    for loader in *loaders
      return false if loader == noom_loader

    table.insert loaders, pos, noom_loader
    true

  compile = (fn) -> with code = __FNDEF_NOOM_CACHE[fn]
    assert code, 'cannot compile function not loaded from noomscript source.'

  {
    :insert_loader
    :compile
  }