Proof-of-concept Server-Sent CSMs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
3.3KB

  1. --
  2. -- A primitive code minifier
  3. --
  4. -- Copyright © 2019 by luk3yx
  5. --
  6. -- Find multiple patterns
  7. local function find_multiple(text, ...)
  8. local n = select('#', ...)
  9. local s, e, pattern
  10. for i = 1, n do
  11. local p = select(i, ...)
  12. local s2, e2 = text:find(p)
  13. if s2 and (not s or s2 < s) then
  14. s, e, pattern = s2, e2 or s2, p
  15. end
  16. end
  17. return s, e, pattern
  18. end
  19. -- Matches
  20. -- These take 2-3 arguments (code, res, char) and should return code and res.
  21. local matches = {
  22. -- Handle multi-line strings
  23. ['%[=*%['] = function(code, res, char)
  24. res = res .. char
  25. char = char:sub(2, -2)
  26. local s, e = code:find(']' .. char .. ']', nil, true)
  27. if not s or not e then return code, res end
  28. return code:sub(e + 1), res .. code:sub(1, e)
  29. end,
  30. -- Handle regular comments
  31. ['--'] = function(code, res, char)
  32. local s, e = code:find('\n', nil, true)
  33. if not s or not e then return '', res end
  34. -- Don't remove copyright or license information.
  35. if e >= 7 then
  36. local first_word = (code:match('^[ \t]*(%w+)') or ''):lower()
  37. if first_word == 'copyright' or first_word == 'license' then
  38. return code:sub(s), res .. char .. code:sub(1, s - 1)
  39. end
  40. end
  41. -- Shift trailing spaces back
  42. local spaces = res:match('(%s*)$') or ''
  43. return spaces .. code:sub(s), res:sub(1, #res - #spaces)
  44. end,
  45. -- Handle multi-line comments
  46. ['%-%-%[=*%['] = function(code, res, char)
  47. char = char:sub(4, -2)
  48. local s, e = code:find(']' .. char .. ']', nil, true)
  49. if not s or not e then return code, res end
  50. -- Shift trailing spaces back
  51. local spaces = res:match('(%s*)$') or ''
  52. return spaces .. code:sub(e + 1), res:sub(1, #res - #spaces)
  53. end,
  54. -- Handle quoted text
  55. ['"'] = function(code, res, char)
  56. res = res .. char
  57. -- Handle backslashes
  58. repeat
  59. local s, e, pattern = find_multiple(code, '\\', char)
  60. if pattern == char then
  61. res = res .. code:sub(1, e)
  62. code = code:sub(e + 1)
  63. elseif pattern then
  64. res = res .. code:sub(1, e + 1)
  65. code = code:sub(e + 2)
  66. end
  67. until not pattern or pattern == char
  68. return code, res
  69. end,
  70. ['%s*[\r\n]%s*'] = function(code, res, char)
  71. return code, res .. '\n'
  72. end,
  73. ['[ \t]+'] = function(code, res, char)
  74. return code, res .. ' '
  75. end,
  76. }
  77. -- Give the functions alternate names
  78. matches["'"] = matches['"']
  79. -- The actual transpiler
  80. return function(code)
  81. assert(type(code) == 'string')
  82. local res = ''
  83. -- Split the code by "tokens"
  84. while true do
  85. -- Search for special characters
  86. local s, e, pattern = find_multiple(code, '[\'"\\]', '%-%-%[=*%[',
  87. '%-%-', '%[=*%[', '%s*[\r\n]%s*', '[ \t]+')
  88. if not s then break end
  89. -- Add non-matching characters
  90. res = res .. code:sub(1, math.max(s - 1, 0))
  91. -- Call the correct function
  92. local char = code:sub(s, e)
  93. local func = matches[char] or matches[pattern]
  94. assert(func, 'No function found for pattern!')
  95. code, res = func(code:sub(e + 1), res, char)
  96. end
  97. return (res .. code):trim()
  98. end