Advertisement
Guest User

speadersheet

a guest
May 24th, 2016
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.47 KB | None | 0 0
  1. --Included libs:
  2. --debug:
  3. local mon = peripheral.wrap("left")
  4. mon.clear()
  5. mon.setCursorPos(1,1)
  6. local function debug(str)
  7. mon.write(str.."\n")
  8. end
  9. --[[
  10. Speadersheet function parser (by me)
  11. ]]
  12. local function split(self, sep)
  13. if sep == nil then
  14. sep = "%s"
  15. end
  16. local t={} ; i=1
  17. for str in string.gmatch(self, "([^"..sep.."]+)") do
  18. t[i] = str
  19. i = i + 1
  20. end
  21. return t
  22. end
  23. local letters = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }
  24. local lettermap = {}
  25. for i=1,#letters do
  26. lettermap[letters[i]] = i
  27. end
  28. local function isFunc(input)
  29. return input:sub(1,1) == "="
  30. end
  31. local function letterToNumber(letter)
  32. return lettermap[letter:lower()]
  33. end
  34. local function numberToLetter(number)
  35. return (letters[number]):upper()
  36. end
  37. local function parseFunc(input) --converts "=Funcname lettercodnumbercode" in {"A1", "B2"}, funcname format
  38. local cells = {}
  39. input = input:sub(2,#input)
  40. local tokens = split(input)
  41. local fname = tokens[1]
  42. for i=2, #tokens do
  43. local token = tokens[i]
  44. local letter = {""}
  45. local number = {""}
  46. local range = false
  47. local comma = false
  48. for c in token:gmatch(".") do
  49. --iterate over characters
  50. if c == "-" then
  51. range = true
  52. elseif c == "," then
  53. comma = true
  54. elseif tonumber(c) then
  55. if range or comma then
  56. number[#number+1] = range and "-" or comma and ","
  57. number[#number+1] = c
  58. range = false
  59. comma = false
  60. else
  61. number[#number] = number[#number] .. c
  62. end
  63. elseif lettermap[c:lower()] then
  64. if range or comma then
  65. letter[#letter+1] = range and "-" or comma and ","
  66. letter[#letter+1] = c
  67. range = false
  68. comma = false
  69. else
  70. letter[#letter] = letter[#letter] .. c
  71. end
  72. end
  73. end
  74. local range, l = false
  75. local nrange = false
  76. local function doNumbers()
  77. for j=1,#number do
  78. local n = number[j]
  79. if n == "," then
  80. --ignore
  81. nrange = false
  82. elseif n == "-" then
  83. nrange = true
  84. elseif nrange then
  85. for num = number[j-2] + 1, n do
  86. cells[#cells+1] = l .. num
  87. end
  88. nrange = false
  89. else
  90. cells[#cells+1] = l .. n
  91. nrange = false
  92. end
  93. end
  94. end
  95. for k=1,#letter do
  96. l = letter[k]
  97. if l == "," then
  98. --ignore
  99. range = false
  100. elseif l == "-" then
  101. range = true
  102. elseif range then
  103. local constl = l
  104. for m = letterToNumber(letter[k-2]) + 1, letterToNumber(constl) do
  105. l = numberToLetter(m)
  106. doNumbers()
  107. end
  108. range = false
  109. else
  110. range = false
  111. doNumbers()
  112. end
  113. end
  114. end
  115. return cells, fname
  116. end
  117. local function convertParsedToIndexes(parsed) --converts the output of parseFunc to {{1,1},{2,2}} format
  118. local token, letter, number
  119. local output = {}
  120. for i=1,#parsed do
  121. token = parsed[i]
  122. letter, number = "",""
  123. for c in token:gmatch(".") do
  124. if tonumber(c) then
  125. number = number .. c
  126. else
  127. letter = letter .. c
  128. end
  129. end
  130. output[i] = {letterToNumber(letter), tonumber(number)}
  131. end
  132. return output
  133. end
  134. --[[
  135. End of speadersheet function parser
  136. ]]
  137. --Setup vars:
  138. --general vars
  139. local filepath = ""
  140. --display vars
  141. local scroll = {0,0}
  142. local showncolumncount = 10
  143. local w,h = term.getSize()
  144. local gridw, gridh = w-2, h-2 --why not put this here :P
  145. local gridx, gridy = 4,2
  146. local defaultColumnWidth = 10
  147. --selection vars
  148. local selected = {1,1} --selected cell position
  149. local endselection = {}
  150. local multiselect = false
  151. --file content vars
  152. local grid = {}
  153. local columnWidths = {}
  154. --copy/paste vars
  155. local clipboard = {}
  156. --theme vars
  157. local theme = term.isColor() and {
  158. default = {colors.white, colors.black},
  159. selected = {colors.green, colors.white},
  160. multiselected = {colors.lime, colors.black},
  161. menu = {colors.green, colors.white},
  162. border = {colors.lightGray, colors.black}
  163. } or {
  164. default = {colors.black, colors.white},
  165. selected = {colors.white, colors.black},
  166. multiselected = {colors.gray, colors.black},
  167. menu = {colors.white, colors.black},
  168. border = {colors.lightGray, colors.black}
  169. }
  170. --Handle args:
  171. local tArgs = {...}
  172. if #tArgs < 1 then --not enough args
  173. print[[
  174. Usage:
  175. speadersheet <filename>
  176. ]]
  177. error("",0)
  178. end
  179. filepath = tArgs[1]
  180. --Local functions:
  181. --[[
  182. Speadersheet function functions + formatting
  183. ]]
  184. local function mytonumber(input)
  185. return tonumber(input) or 0
  186. end
  187. local spfunctions = {
  188. ADD = function(currently, nextnum)
  189. return mytonumber(currently) + mytonumber(nextnum)
  190. end;
  191. COPY = function(currently) return currently end;
  192.  
  193. }
  194. local function doFunction(name, indexes)
  195. if spfunctions[name] then
  196. local func = spfunctions[name]
  197. local current, output
  198. for i=1, #indexes do
  199. current = grid[indexes[i][1]] and grid[indexes[i][1]][indexes[i][2]] and grid[indexes[i][1]][indexes[i][2]] or ""
  200. if i > 1 then
  201. output = func(output, current)
  202. else
  203. output = current
  204. end
  205. end
  206. return output
  207. else
  208. return "ERR:" .. name
  209. end
  210. end
  211. local function formatText(text, cwidth)
  212. while isFunc(text) do
  213. local parsed, func = parseFunc(text)
  214. text = tostring(doFunction(func, convertParsedToIndexes(parsed)))
  215. end
  216. if #text >= cwidth then
  217. return text:sub(1,cwidth)
  218. elseif tonumber(text) then
  219. return string.rep(" ",(cwidth) - #text) .. text
  220. else
  221. return text .. string.rep(" ",(cwidth) - #text)
  222. end
  223. end
  224. --[[
  225. Other functions
  226. ]]
  227. local function selectColors(name) --selects colors, args: name of theme
  228. local cols = theme[name]
  229. term.setBackgroundColor(cols[1])
  230. term.setTextColor(cols[2])
  231. end
  232. local function redrawGrid() --redraws grid and bottom line
  233. --this is the function that will need lots of optimization probably
  234. local drawing = {scroll[1], scroll[2]} --which cell are we drawing (to get the content)
  235. for y=gridy, gridh + gridy - 1 do
  236. drawing[2] = drawing[2] + 1
  237. drawing[1] = scroll[1]
  238. term.setCursorPos(gridx, y)
  239. for i=1,showncolumncount do
  240. drawing[1] = drawing[1] + 1
  241. local cwidth = columnWidths[drawing[1]] or defaultColumnWidth
  242. selectColors((selected[1] == drawing[1] and selected[2] == drawing[2]) and "selected" or (multiselect and (selected[1] >= drawing[1] and selected[2] >= drawing[2]) and (endselection[1] <= drawing[1] and endselection[2] <= drawing[2])) and "multiselected" or "default")
  243. local realtext = grid[drawing[1]] and grid[drawing[1]][drawing[2]] and grid[drawing[1]][drawing[2]] or ""
  244. local text = formatText(realtext, cwidth - 1)
  245. term.write(text)
  246. if selected[1] == drawing[1] and selected[2] == drawing[2] then
  247. local oldx,oldy = term.getCursorPos()
  248. term.setCursorPos(1,h)
  249. selectColors("menu")
  250. --This is repeated because of functions later
  251. term.write(realtext .. (#realtext < w and string.rep(" ",w - #realtext) or ""))
  252. term.setCursorPos(oldx,oldy)
  253. end
  254. selectColors("border")
  255. term.write(" ")
  256. end
  257. end
  258. end
  259. local function redrawMenu() --redraws column and row numbers
  260. --redraw top column numbers
  261. do
  262. local drawing = scroll[1]
  263. term.setCursorPos(gridx, 1)
  264. selectColors("menu")
  265. term.clearLine()
  266. term.setCursorPos(gridx, 1)
  267. for i=1,showncolumncount do
  268. drawing = drawing + 1
  269. local cwidth = columnWidths[drawing] or defaultColumnWidth
  270. term.write(tostring(drawing))
  271. term.write(string.rep(" ",cwidth - #tostring(drawing)))
  272. end
  273. end
  274. --redraw line numbers
  275. local drawing = scroll[2]
  276. for y=gridy, gridh + gridy - 1 do
  277. drawing = drawing + 1
  278. term.setCursorPos(1, y)
  279. term.write(tostring(drawing) .. string.rep(" ",3 - #tostring(drawing)))
  280. end
  281. end
  282. local function checkScroll() --scrolls if needed
  283. local scrolled = false
  284. --horizontal:
  285. local cx = gridx
  286. local count = 0
  287. local drawing = scroll[1]
  288. for i=1,w do -- count how many columns are on screen
  289. drawing = drawing + 1
  290. local cwidth = columnWidths[drawing] or defaultColumnWidth
  291. cx = cx + cwidth
  292. count = count + 1
  293. if cx >= w then break end
  294. end
  295. showncolumncount = count
  296. while selected[1] - scroll[1] < 1 do
  297. scroll[1] = scroll[1] - 1
  298. scrolled = true
  299. end
  300. while selected[1] > scroll[1] + count do
  301. scroll[1] = scroll[1] + 1
  302. scrolled = true
  303. end
  304. --vertical:
  305. while selected[2] - scroll[2] < 1 do
  306. scroll[2] = scroll[2] - 1
  307. scrolled = true
  308. end
  309. while selected[2] > scroll[2] + gridh do
  310. scroll[2] = scroll[2] + 1
  311. scrolled = true
  312. end
  313. if scrolled then
  314. redrawGrid()
  315. redrawMenu()
  316. end
  317. end
  318. local function save() --saves persistant data
  319. local h = fs.open(filepath,"w")
  320. local cont = textutils.serialize{
  321. grid,
  322. selected,
  323. endselection,
  324. multiselect,
  325. columnWidths,
  326. showhelp,
  327. scroll
  328. }
  329. h.write(cont)
  330. h.close()
  331. end
  332. local function reload() --loads persistant data
  333. if fs.exists(filepath) then
  334. local h = fs.open(filepath,"r")
  335. local cont = textutils.unserialize(h.readAll())
  336. h.close()
  337. grid = cont[1] or {}
  338. selected = cont[2] or {1,1}
  339. endselection = cont[3] or {1,1}
  340. multiselect = cont[4]
  341. columnWidths = cont[5] or {}
  342. showhelp = cont[6] or true
  343. scroll = cont[7] or {0,0}
  344. end
  345. end
  346. local function cancel() --cancels multiselection
  347. if multiselect then
  348. multiselect = false
  349. end
  350. endselection = {selected[1], selected[2]}
  351. end
  352. local function initMultiselect() --inits multiselection if needed
  353. if not multiselect then
  354. multiselect = true
  355. endselection = {selected[1], selected[2]}
  356. end
  357. end
  358. local function convertSelection() -- converts multi/single selection to a smaller sub grid and returns it
  359. local subx, suby = 1,1
  360. local subgrid = {}
  361. for x = endselection[1], selected[1] do
  362. subgrid[subx] = {}
  363. for y = endselection[2], selected[2] do
  364. subgrid[subx][suby] = grid[x] and grid[x][y] and grid[x][y] or ""
  365. suby = suby + 1
  366. end
  367. suby = 1
  368. subx = subx + 1
  369. end
  370. return subgrid
  371. end
  372. local function pasteSubgrid(subgrid, x, y) --pastes subgrid in main grid
  373. for i=#subgrid, 1, -1 do
  374. local line = subgrid[i]
  375. for j = #line, 1, -1 do
  376. local cell = line[j]
  377. if not grid[x + i - 1] then
  378. grid[x + i - 1] = {}
  379. end
  380. grid[x + i - 1][y + j - 1] = cell
  381. end
  382. end
  383. end
  384. local function deleteSelection()
  385. for x = endselection[1], selected[1] do
  386. for y = endselection[2], selected[2] do
  387. if grid[x] then
  388. grid[x][y] = ""
  389. end
  390. end
  391. end
  392. end
  393. reload()
  394. --Key-event table:
  395. local handler = {
  396. --[[
  397. movement:
  398. arrow keys = move
  399. p/l = multiselect (move right bottom corner right/down)
  400. enter = edit
  401. home = first column
  402. end = first row
  403. page up = page up
  404. page down = page down
  405. general:
  406. s = save
  407. q = exit
  408. copy/paste:
  409. c = copy
  410. v = paste
  411. x = cut
  412. ]]
  413. key = {
  414. [keys.up] = function() cancel() selected[2] = selected[2] > 1 and selected[2] - 1 or 1 redrawGrid() end;
  415. [keys.down] = function() cancel() selected[2] = selected[2] + 1 redrawGrid() end;
  416. [keys.left] = function() cancel() selected[1] = selected[1] > 1 and selected[1] - 1 or 1 redrawGrid() end;
  417. [keys.right] = function() cancel() selected[1] = selected[1] + 1 redrawGrid() end;
  418. [keys.home] = function() cancel() selected[1] = 1 redrawGrid() end;
  419. [keys["end"]] = function() cancel() selected[2] = 1 redrawGrid() end;
  420. [keys.pageUp] = function() cancel() selected[2] = selected[2] > gridh-1 and selected[2] - (gridh-1) or 1 redrawGrid() end;
  421. [keys.pageDown] = function() cancel() selected[2] = selected[2] + gridh - 1 redrawGrid() end;
  422. [keys.p] = function() initMultiselect() selected[1] = selected[1] + 1 redrawGrid() end;
  423. [keys.l] = function() initMultiselect() selected[2] = selected[2] + 1 redrawGrid() end;
  424. [keys.enter] = function()
  425. term.setCursorPos(1,h)
  426. selectColors("menu")
  427. term.clearLine()
  428. term.setCursorPos(1,h)
  429. if not grid[selected[1]] then
  430. grid[selected[1]] = {}
  431. end
  432. grid[selected[1]][selected[2]] = read()
  433. redrawGrid()
  434. redrawMenu()
  435. end;
  436. [keys.s] = function()
  437. save()
  438. end;
  439. [keys.q] = function()
  440. term.setBackgroundColor(colors.black)
  441. term.setTextColor(colors.white)
  442. term.clear()
  443. term.setCursorPos(1,1)
  444. sleep(0)
  445. error("",0)
  446. end;
  447. [keys.c] = function()
  448. clipboard = convertSelection()
  449. end;
  450. [keys.v] = function()
  451. pasteSubgrid(clipboard, selected[1], selected[2])
  452. redrawGrid()
  453. end;
  454. [keys.x] = function()
  455. clipboard = convertSelection()
  456. deleteSelection()
  457. redrawGrid()
  458. end;
  459. }
  460. }
  461. --Main loop:
  462. term.clear()
  463. redrawGrid()
  464. redrawMenu()
  465. while true do
  466. local event,param = os.pullEvent()
  467. if handler[event] and handler[event][param] then
  468. handler[event][param]()
  469. checkScroll()
  470. end
  471. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement