aboutsummaryrefslogtreecommitdiff
path: root/files.lua
blob: ded403a5aa23cdf2d44598839f7d22cc01c3cee3 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
local lpeg = require('lpeg')
local re = require('re')
local files = { }
local _FILE_CACHE = { }
files.spoof = function(filename, contents)
  _FILE_CACHE[filename] = contents
end
files.read = function(filename)
  do
    local file_contents = _FILE_CACHE[filename]
    if file_contents then
      return file_contents
    end
  end
  local file = io.open(filename)
  if package.nomsupath and not file then
    for nomsupath in package.nomsupath:gmatch("[^;]+") do
      file = io.open(nomsupath .. "/" .. filename)
      if file then
        break
      end
    end
  end
  if not (file) then
    return nil
  end
  local contents = file:read("*a")
  file:close()
  _FILE_CACHE[filename] = contents
  return contents
end
local iterate_single
iterate_single = function(item, prev)
  if item == prev then
    return nil
  else
    return item
  end
end
local match, gsub
do
  local _obj_0 = string
  match, gsub = _obj_0.match, _obj_0.gsub
end
iterate_single = function(item, prev)
  if item == prev then
    return nil
  else
    return item
  end
end
local ok, lfs = pcall(require, "lfs")
if ok then
  files.walk = function(path)
    local browse
    browse = function(filename)
      local file_type = lfs.attributes(filename, 'mode')
      if file_type == 'file' then
        if match(filename, "%.nom$") or match(filename, "%.lua$") then
          coroutine.yield(filename)
          return true
        end
      elseif file_type == 'directory' then
        for subfile in lfs.dir(filename) do
          if not (subfile == "." or subfile == ".." or not subfile:match("%.nom$")) then
            browse(filename .. "/" .. subfile)
          end
        end
        return true
      elseif file_type == 'char device' then
        coroutine.yield(filename)
        return true
      end
      return false
    end
    return coroutine.wrap(function()
      if not browse(path) and package.nomsupath then
        for nomsupath in package.nomsupath:gmatch("[^;]+") do
          if browse(nomsupath .. "/" .. path) then
            break
          end
        end
      end
      return nil
    end)
  end
else
  if io.popen('find . -maxdepth 0'):close() then
    error("Could not find 'luafilesystem' module and couldn't run system command `find` (this might happen on Windows). Please install `luafilesystem` (which can be found at: http://keplerproject.github.io/luafilesystem/ or `luarocks install luafilesystem`)", 0)
  end
  files.walk = function(path)
    if match(path, "%.nom$") or match(path, "%.lua$") or match(path, "^/dev/fd/[012]$") then
      return iterate_single, path
    end
    path = gsub(path, "\\", "\\\\")
    path = gsub(path, "`", "")
    path = gsub(path, '"', '\\"')
    path = gsub(path, "$", "")
    return coroutine.wrap(function()
      local f = io.popen('find -L "' .. path .. '" -not -path "*/\\.*" -type f -name "*.nom"')
      local found = false
      for line in f:lines() do
        found = true
        coroutine.yield(line)
      end
      if not found and package.nomsupath then
        f:close()
        for nomsupath in package.nomsupath:gmatch("[^;]+") do
          f = io.popen('find -L "' .. package.nomsupath .. '/' .. path .. '" -not -path "*/\\.*" -type f -name "*.nom"')
          for line in f:lines() do
            found = true
            coroutine.yield(line)
          end
          f:close()
          if found then
            break
          end
        end
      end
      if not (found) then
        return error("Invalid file path: " .. tostring(path))
      end
    end)
  end
end
local line_counter = re.compile([[    lines <- {| line (%nl line)* |}
    line <- {} (!%nl .)*
]], {
  nl = lpeg.P("\r") ^ -1 * lpeg.P("\n")
})
local get_lines = re.compile([[    lines <- {| line (%nl line)* |}
    line <- {[^%nl]*}
]], {
  nl = lpeg.P("\r") ^ -1 * lpeg.P("\n")
})
local _LINE_STARTS = { }
files.get_line_starts = function(str)
  if type(str) ~= 'string' then
    str = tostring(str)
  end
  do
    local starts = _LINE_STARTS[str]
    if starts then
      return starts
    end
  end
  local line_starts = line_counter:match(str)
  _LINE_STARTS[str] = line_starts
  return line_starts
end
files.get_line_number = function(str, pos)
  local line_starts = files.get_line_starts(str)
  local lo, hi = 1, #line_starts
  while lo <= hi do
    local mid = math.floor((lo + hi) / 2)
    if line_starts[mid] > pos then
      hi = mid - 1
    else
      lo = mid + 1
    end
  end
  return hi
end
return files