Advertisement
9551

Untitled

Dec 10th, 2023 (edited)
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.47 KB | None | 0 0
  1. local expect = require("cc.expect").expect
  2.  
  3. local TEMPLATE_TYPE = {}
  4.  
  5. local function lookupify(tbl)
  6. local lookup = {}
  7. for k,v in pairs(tbl) do lookup[v] = k end
  8. return lookup
  9. end
  10.  
  11. local function combined_lookup(a,b)
  12. local lookup = {}
  13. for k,v in pairs(b) do lookup[a[k]] = v end
  14. return lookup
  15. end
  16.  
  17. local token_translations = {elif="elseif"}
  18.  
  19. local keywords = {
  20. "and", "break", "do", "else", "elseif", "elif",
  21. "end", "false", "for", "function","if",
  22. "in", "local", "nil", "not", "or",
  23. "repeat","return","then","true", "until","while"
  24. }
  25.  
  26. local lua_tokens = {
  27. "+", "-", "*", "/", "%", "^","#",
  28. "==","~=","<=",">=","<", ">","=",
  29. "(", ")", "{", "}", "[", "]",
  30. ";", ":", ",", ".", "..","..."
  31. }
  32.  
  33. local keyword_value_proccessor = {
  34. value={
  35. ["true"] =function() return true end,
  36. ["false"]=function() return false end,
  37. ["nil"] =function() return nil end
  38. },
  39. }
  40.  
  41. local expansible_tokens = {
  42. ["="] ="=",
  43. ["=="] ="=",
  44. ["~"] ="=",
  45. ["~="] ="=",
  46. ["<"] ="=",
  47. ["<="] ="=",
  48. [">"] ="=",
  49. [">="] ="=",
  50. ["."] =".",
  51. [".."] =".",
  52. ["..."] ="",
  53. ["-"] ="-",
  54. ["--"] ="[",
  55. ["["] ="[",
  56. ["[["] ="[",
  57. ["]"] ="]",
  58. ["]]"] ="]",
  59. ["--["] ="[",
  60. ["--[["]="["
  61. }
  62.  
  63. local scope = {
  64. open_begin = lookupify{
  65. "if","elseif","while","for"
  66. },
  67. open = lookupify{
  68. "do","else","then","function","repeat"
  69. },
  70. close = lookupify{
  71. "end","elseif","else","until"
  72. }
  73. }
  74.  
  75. local positioning_types = {
  76. "HEAD","TAIL","THIS","WIPE"
  77. }
  78. local positioning_handles = {
  79. function(tree,position,type,simple_override)
  80. if not simple_override then
  81. local tree_size = #tree
  82. for i=tree_size,position,-1 do
  83. local val = tree[i]
  84.  
  85. local hook = val.hook_info
  86. if hook then hook.index = hook.index + 1 end
  87.  
  88. tree[i+1] = val
  89. end
  90. end
  91.  
  92. tree[position] = type
  93. end,
  94. function(tree,position,type,simple_override,tail_handle)
  95. if not simple_override then
  96. local tree_size = #tree
  97. for i=tree_size,position,-1 do
  98. local val = tree[i]
  99.  
  100. local hook = val.hook_info
  101. if hook then hook.index = hook.index + 1 end
  102.  
  103. tree[i+1] = val
  104. end
  105. end
  106.  
  107. tree[position+(tail_handle or 0)] = type
  108. end,
  109. function(tree,position,type)
  110. tree[position] = type
  111. end,
  112. function(tree,position)
  113. local tree_size = #tree
  114. tree[position] = nil
  115. for i=position+1,tree_size do
  116. local val = tree[i]
  117.  
  118. local hook = val.hook_info
  119. if hook then hook.index = hook.index - 1 end
  120.  
  121. tree[i-1] = val
  122. end
  123. tree[tree_size] = nil
  124. end
  125. }
  126.  
  127. local positioning_offsets = {
  128. 0,1,1,0
  129. }
  130.  
  131. local hook_types = {
  132. "#","$","="
  133. }
  134. local hook_type_handles = {
  135. function(tree,position,type,positioning)
  136. positioning(tree,position,type)
  137. end,
  138. function(tree,position,type,positioning)
  139. positioning(tree,position-1,type,true)
  140. end,
  141. function(tree,position,type,positioning)
  142. positioning(tree,position-1,type,true,1)
  143. end
  144. }
  145.  
  146. local hooks = combined_lookup(hook_types,hook_type_handles)
  147. local relative_positions = combined_lookup(positioning_types,positioning_handles)
  148. local relative_offsets = combined_lookup(positioning_types,positioning_offsets)
  149.  
  150. local keyword_lookup = lookupify(keywords)
  151. local token_lookup = lookupify(lua_tokens)
  152.  
  153. local TOKEN_MT = {__tostring=function(self) return "TOKEN: " .. self.type end}
  154. local SCOPE_MT = {__tostring=function(self) return "SCOPE: " .. self.index end}
  155.  
  156. local HOOK_MT = {__index={
  157. set_type = function(self,type)
  158. self.type = type
  159. end
  160. },__tostring=function(self) return "HOOK: " .. self.name end}
  161.  
  162. local function make_value(token,token_buffer,token_index)
  163. local out
  164. if keyword_lookup[token] then
  165. local keyword_type = keyword_lookup[token]
  166. if keyword_type and keyword_value_proccessor[keyword_type] then
  167. out = keyword_value_proccessor[keyword_type][token](token_buffer,token_index)
  168. end
  169. elseif token_lookup[token] then
  170. out = "lua_token"
  171. elseif token:match("^%-%-") or token:match("^%-%-%[%[.+%]%]$") then
  172. out = token:match("^%-%-%[%[(.+)%]%]$") or token:match("^%-%-(.+)")
  173. elseif token:match("^\".+\"$") or token:match("^%[%[.+%]%]$") then
  174. out = token:match("^%[%[(.+)%]%]$") or token:match("^\"(.+)\"$")
  175. elseif tonumber(token) ~= nil then
  176. out = tonumber(token)
  177. else out = token end
  178.  
  179. return out
  180. end
  181.  
  182. local function make_type(token)
  183. local out
  184. local keyword_type
  185. if keyword_lookup[token] then
  186. out = "lua_keyword"
  187. keyword_type = keyword_lookup[token]
  188. elseif token_lookup[token] then
  189. out = "lua_token"
  190. elseif token:match("^%-%-") or token:match("^%-%-%[%[.+%]%]$") then
  191. out = "comment"
  192. elseif token:match("^\".+\"$") or token:match("^%[%[.+%]%]$") then
  193. out = "string"
  194. elseif tonumber(token) ~= nil then
  195. out = "number"
  196. else out = "name" end
  197.  
  198. return out,keyword_type
  199. end
  200.  
  201. local function parse_token(out,token,token_buffer,token_index)
  202. out.entry = "token"
  203. out.name = token
  204.  
  205. out.type,out.keyword_type = make_type (token)
  206. out.value = make_value(token,token_buffer,token_index)
  207.  
  208. setmetatable(out,TOKEN_MT)
  209.  
  210. return out
  211. end
  212.  
  213. local RAW_TOKEN_MT = {
  214. __index = {
  215. parse=function(this)
  216. local reference = {}
  217. return parse_token(reference,this:get())
  218. end,
  219. get=function(this)
  220. return this.str_token
  221. end,
  222. rawtoken = true
  223. },
  224. __tostring=function(this) return "RAW_TOKEN: " .. this:get() end
  225. }
  226.  
  227. local function make_raw_token(str_token)
  228. if token_translations[str_token] then str_token = token_translations[str_token] end
  229. return setmetatable({str_token=str_token},RAW_TOKEN_MT)
  230. end
  231.  
  232. local function generate_tokens(str,objectify_tokens)
  233. expect(1,str,"string")
  234.  
  235. str = str .. "\0"
  236. local tokens = {}
  237. local token = ""
  238.  
  239. local is_string = false
  240. local is_number = false
  241. local is_comment = false
  242. local is_mulline = false
  243. local escape_next = false
  244.  
  245. local can_expand = ""
  246.  
  247. for i=1,#str-1 do
  248. local char = str:sub(i,i)
  249. local next_char = str:sub(i+1,i+1)
  250.  
  251. if (char == "\'" or char == "\"") and not is_comment then
  252. if not escape_next then is_string = not is_string end
  253. end
  254. if char == "\\" then
  255. escape_next = true
  256. end
  257. if char == "\n" and not is_mulline then is_string,is_comment,is_number = false,false,false end
  258.  
  259. if not is_string and (char:match("%d") or (char == "." and next_char:match("%d"))) then
  260. is_number = true
  261. elseif char ~= "." then
  262. is_number = false
  263. end
  264.  
  265. if char:match("[%s\t]") and not is_string and not is_comment and not is_number then
  266. if token ~= "" then tokens[#tokens+1] = token end
  267. token = ""
  268. elseif token_lookup[char] and not is_string and not is_number and not is_comment then
  269. if token ~= "" then tokens[#tokens+1] = token end
  270.  
  271. if not expansible_tokens[char] then
  272. tokens[#tokens+1] = char
  273. end
  274.  
  275. token = ""
  276. elseif not expansible_tokens[char] or is_string or is_number or is_comment then
  277. token = token .. char
  278. escape_next = false
  279. end
  280.  
  281. if (((expansible_tokens[can_expand] == char) or (can_expand == "" and expansible_tokens[char]))) and not is_number then
  282. can_expand = can_expand .. char
  283. if not expansible_tokens[can_expand .. next_char] then
  284. if can_expand == "--" and not is_string and not is_number and not is_comment then
  285. token = token .. "--"
  286. is_comment = true
  287. elseif (can_expand == "[[" or can_expand == "--[[") and not is_string and not is_number and not is_comment then
  288. token = token .. can_expand
  289. if can_expand == "--[[" and not escape_next then
  290. is_comment = true
  291. elseif can_expand == "[[" and not escape_next then
  292. is_string = true
  293. end
  294. is_mulline = true
  295. elseif can_expand == "]]" and is_mulline and not escape_next then
  296. tokens[#tokens+1] = token
  297. token = ""
  298. is_mulline,is_string,is_comment,is_number = false,false,false,false
  299. elseif not is_string and not is_number and not is_comment then
  300. tokens[#tokens+1] = can_expand
  301. end
  302. can_expand = ""
  303. end
  304. end
  305. end
  306.  
  307. if token ~= "" then tokens[#tokens+1] = token end
  308.  
  309. for k,v in ipairs(tokens) do
  310. if objectify_tokens then
  311. tokens[k] = make_raw_token(v)
  312. elseif token_translations[v] then
  313. tokens[k] = token_translations[v]
  314. else
  315. tokens[k] = v
  316. end
  317. end
  318.  
  319. return tokens
  320. end
  321.  
  322. local function generate_extra_hook_info(t)
  323. local hook = t.scope[t.index]
  324. hook.hook_info = t
  325.  
  326. t.hook_comment = hook
  327. t.name = hook.value:match("^.(.+)")
  328. t.type = hook.value:match("^.")
  329.  
  330. return setmetatable(t,HOOK_MT)
  331. end
  332.  
  333. local function find_hooks(tree,lst)
  334. lst = lst or {}
  335. for k,v in ipairs(tree) do
  336. if v.entry == "scope" then
  337. find_hooks(v,lst)
  338. elseif v.type == "comment" then
  339. local new_index = #lst+1
  340. local new = generate_extra_hook_info{scope=tree,index=k,hook_index=new_index}
  341. if hooks[new.type] then
  342. lst[new_index] = new
  343. end
  344. end
  345. end
  346.  
  347. return lst
  348. end
  349.  
  350. local function generate_code_tree(tokens)
  351. local current_scope = {}
  352.  
  353. local token_buffer = {}
  354. local buffer_open = false
  355.  
  356. local scope_index = 0
  357.  
  358. for i=1,#tokens do
  359. local token_tmp = tokens[i]
  360.  
  361. local current_token = token_tmp.rawtoken and token_tmp:get() or (token_tmp.name or token_tmp)
  362.  
  363. if scope.open_begin[current_token] then
  364. buffer_open = true
  365. end
  366. if buffer_open then
  367. token_buffer[#token_buffer+1] = current_token
  368. end
  369.  
  370. if scope.close[current_token] then
  371. current_scope = current_scope.parent
  372. end
  373.  
  374. current_scope[#current_scope+1] = parse_token({},current_token,nil,i)
  375.  
  376. if scope.open[current_token] then
  377.  
  378. scope_index = scope_index + 1
  379.  
  380. local new_scope = setmetatable({
  381. index = scope_index,
  382. parent = current_scope,
  383. keyword = current_token,
  384. name = current_token,
  385. entry = "scope"
  386. },SCOPE_MT)
  387.  
  388. token_buffer = {}
  389. buffer_open = false
  390.  
  391. current_scope[#current_scope+1] = new_scope
  392.  
  393. current_scope = new_scope
  394. end
  395. end
  396.  
  397. return current_scope
  398. end
  399.  
  400. local function format_code_block(code)
  401. return code
  402. end
  403.  
  404. local function generate_code(tree,is_layer)
  405. local code = ""
  406.  
  407. for k,v in ipairs(tree) do
  408. if v.entry == "scope" then
  409. code = code .. generate_code(v,true) .. "\n"
  410. else
  411. code = code .. v.name .. (v.type == "comment" and "\n" or " ")
  412. end
  413. end
  414.  
  415. return is_layer and code or format_code_block(code)
  416. end
  417.  
  418. local function load_template_tree(tree)
  419. local template_hooks = find_hooks(tree)
  420.  
  421. local object;object = {
  422. inject=function(hook_list,position,code)
  423. local code_tree = code
  424. local rebuild_on_end = false
  425.  
  426. if type(hook_list) ~= "table" then
  427. error("Invalid hook",2)
  428. end
  429. if type(code_tree) == "table" and code_tree.type == TEMPLATE_TYPE then
  430. code_tree = code.tree
  431. rebuild_on_end = true
  432. end
  433.  
  434. for k,hook in ipairs(hook_list) do
  435. local scope = hook.scope
  436. for k,v in ipairs(code_tree) do
  437. hooks[hook.type](scope,hook.index+k*position.offset,v,position.pos)
  438. end
  439. end
  440.  
  441. if rebuild_on_end then object.rebuild() end
  442. return object
  443. end,
  444. apply_patches=function(input,layered)
  445. return generate_code(input or tree,layered)
  446. end,
  447. rebuild = function()
  448. local reconstructed = load_template_tree(tree)
  449.  
  450. for k,v in pairs(reconstructed) do
  451. object[k] = v
  452. end
  453.  
  454. return reconstructed
  455. end,
  456. tree = tree,
  457. type = TEMPLATE_TYPE
  458. }
  459.  
  460. for k,v in pairs(template_hooks) do
  461. local index_name = ("_%s"):format(v.name)
  462.  
  463. if not object[index_name] then object[index_name] = {} end
  464. local hook_named = object[index_name]
  465.  
  466. hook_named[#hook_named+1] = v
  467. end
  468.  
  469. return object
  470. end
  471.  
  472. local function load_template_tokens(tokens)
  473. return load_template_tree(generate_code_tree(tokens))
  474. end
  475.  
  476. local function generate_from_tokens(tokens,block_format)
  477. return generate_code(generate_code_tree(tokens),block_format)
  478. end
  479.  
  480. local function load_template(data)
  481. return load_template_tokens(generate_tokens(data))
  482. end
  483.  
  484. local function load_template_file(path)
  485. local file = fs.open(path,"r")
  486. local data = file.readAll()
  487.  
  488. file.close()
  489.  
  490. return load_template_tokens(generate_tokens(data))
  491. end
  492.  
  493. local function parse_code_block(data)
  494. local tokens = generate_tokens (data)
  495. local tree = generate_code_tree(tokens)
  496.  
  497. return tree
  498. end
  499.  
  500. local function inject_table_position(tp)
  501. return {pos=relative_positions[tp],offset=relative_offsets[tp]}
  502. end
  503.  
  504. local function label_tokens(tokenized_source)
  505. local out = {}
  506.  
  507. for token_index=1,#tokenized_source do
  508. local current_token = tokenized_source[token_index]
  509.  
  510. out[token_index] = parse_token({},current_token)
  511. end
  512.  
  513. return out
  514. end
  515.  
  516. return {
  517. new_patch = load_template,
  518. new_patch_from_file = load_template_file,
  519. new_patch_from_tokens = load_template_tokens,
  520. new_patch_from_compiled = load_template_tree,
  521. generate_from_tokens = generate_from_tokens,
  522. generate_from_tree = generate_code,
  523. build_raw_token = make_raw_token,
  524. compile_code = parse_code_block,
  525. tokenize_source = generate_tokens,
  526. label_tokens = label_tokens,
  527. format_code = format_code_block,
  528. tree_from_tokens = generate_code_tree,
  529. compile_tokens = generate_code_tree,
  530. parse_token = parse_token,
  531. At = inject_table_position,
  532. data = {
  533. keyword_lookup = keyword_lookup,
  534. token_lookup = token_lookup,
  535. scope = {
  536. open_begin = scope.open_begin,
  537. open = scope.open,
  538. close = scope.close
  539. }
  540. }
  541. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement