Guest User

Untitled

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